AI primitive
ChatComposer
The textarea-based prompt input every modern AI product needs. Ships with model picker, reasoning toggle, character counter, optimistic submit state, and a stop-mid-stream button. Wire your model SDK at onSubmit.
Usage
Pick your edition with the tabs. Selection syncs across this page and the rest of the docs.
import { ChatComposer } from '@/components/organisms'
export default function Page() {
return (
<ChatComposer
layout="standard"
models={[
{ id: 'sonnet', label: 'Claude Sonnet 4.6', hint: 'Most capable' },
{ id: 'haiku', label: 'Claude Haiku 4.5', hint: 'Fastest' },
]}
defaultModel="sonnet"
reasoningToggle
onSubmit={async ({ prompt, model, reasoning }) => {
// Wire your model SDK here. Server action, fetch, anything.
// The composer handles its own optimistic state.
}}
/>
)
}Props
Same surface in both editions; the HTML version exposes equivalents through Alpinex-data.
| Prop | Type | Default | Description |
|---|---|---|---|
| layout | "standard" | "compact" | "standard" | Visual density. Compact reduces padding for sidebar mounts. |
| models | Array<{id, label, hint?}> | [] | Models the user can switch between. Empty array hides the picker. |
| defaultModel | string | first model id | Initial selection. |
| reasoningToggle | boolean | false | Show the reasoning-on/off toggle next to the submit button. |
| maxLength | number | 4000 | Hard limit on prompt characters. Visible counter at 75% threshold. |
| onSubmit | (payload) => void | Promise<void> | — | Called with { prompt, model, reasoning }. Composer disables itself while a returned promise pends. |
| onStop | () => void | — | Called when the user clicks the stop button mid-stream. Optional. |
Accessibility
role
Wrapper is a <form>; submit fires on Enter (Shift+Enter inserts newline).
keyboard
Cmd/Ctrl+Enter submits regardless of focus inside the textarea.
live
Submit/disabled state announces via aria-live="polite" on the status row.
contrast
All text meets WCAG AA in both light and dark, including the placeholder.
motion
The pending shimmer respects prefers-reduced-motion (drops to a static dim).
Read next
- StreamingResponse — pair the composer with a response card that streams tokens.
- ModelPicker — standalone model picker if you need it outside the composer.