Composer
Multi-line input + submission shell for chat. Submits on Enter (Shift+Enter inserts newline). Trailing slot for attachment + send buttons.
Basic — round-trip with local message state
Ask anything — I'll echo your input back.
With attachment slot — chips render in the trailing children region
- assistant
Attach files, then send — your message + chips render above.
Installation
pnpm dlx @hex-core/cli add composerWith attachment slot
Attach button on the leading edge of the trailing slot.
<Composer value={v} onValueChange={setV} onSubmit={send}>
<Button variant="ghost" size="icon" onClick={pickFile}><Paperclip /></Button>
<Button type="submit">Send</Button>
</Composer>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | string | — | Controlled textarea value. |
onValueChangerequired | function | — | Called with the new value on each keystroke. |
onSubmitrequired | function | — | Called with the trimmed value on Enter or form submit. |
disabled | boolean | false | Lock the input and suppress submission (e.g. during streaming). |
placeholder | string | — | Textarea placeholder copy. |
submitOnEnter | boolean | true | Submit when Enter is pressed without Shift. Disable to require button click. |
children | ReactNode | — | Trailing slot — attachment buttons, voice toggle, send button, etc. |
className | string | — | Additional CSS classes on the form wrapper. |
AI Guidance
When to use
Wrap any user-input surface in an AI app — chatbots, AI editors, agent prompts. Pair with `useChat` from @ai-sdk/react or any equivalent state hook.
When not to use
Don't use for non-chat forms (use Form + Textarea). Don't bake fetch/streaming logic into onSubmit — keep the network call in the consumer.
Common mistakes
- Calling onSubmit with the raw event instead of the value — onSubmit receives the trimmed string already
- Forgetting to clear `value` after submit — Composer is fully controlled
- Wrapping the component in <form> — Composer renders its own form element
Accessibility
Renders a real <form> + <textarea>, so Enter submission and screen-reader announcements work without extra ARIA. Pass `aria-label` on the wrapper if there's no visible label.
Related components
Token budget: 280
Verified against @hex-core/components@1.12.0