Fields
Composer
The message input of a chat: Enter sends, Shift+Enter adds a line, stop interrupts a reply.
When to use it: Use it under Messages for any chat or assistant surface. For a one-off multi-line field use TextArea.
Example
import { useState } from "react";
import { Composer, ComposerInput, ComposerSend, ComposerStop } from "@comp0/react";
export function Example() {
const [sent, setSent] = useState<string[]>([]);
const [generating, setGenerating] = useState(false);
return (
<div className="flex w-full max-w-sm flex-col gap-3">
<Composer
generating={generating}
onSend={(text) => {
setSent((current) => [...current, text]);
setGenerating(true);
}}
onStop={() => setGenerating(false)}
className="flex items-end gap-2"
>
<ComposerInput
aria-label="Message"
placeholder="Message the assistant"
className="min-h-11 min-w-0 flex-1 resize-none rounded border border-zinc-950/10 bg-white px-3 py-2.5 text-base text-zinc-950 outline-teal-600 focus-visible:outline-2 sm:py-2 sm:text-sm dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-50 dark:outline-teal-400"
/>
<ComposerSend className="rounded bg-teal-600 px-3 py-2.5 text-base text-white outline-teal-600 focus-visible:outline-2 disabled:opacity-50 sm:py-2 sm:text-sm dark:bg-teal-500 dark:text-zinc-950 dark:outline-teal-400">
Send
</ComposerSend>
<ComposerStop className="rounded border border-zinc-950/10 bg-white px-3 py-2 text-base text-zinc-900 outline-teal-600 focus-visible:outline-2 disabled:opacity-50 sm:text-sm dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-100 dark:outline-teal-400">
Stop
</ComposerStop>
</Composer>
<p className="text-sm text-zinc-600 dark:text-zinc-400">
Enter sends, Shift+Enter adds a line. Sent: {sent.length}
</p>
</div>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
Composer
Native form that owns the draft. Owns a DOM element.
ComposerInput
The textarea; Enter sends unless Shift or Alt is held or an IME composition is active. Owns a DOM element.
ComposerSend
Submit button, disabled while the draft is empty or a reply is generating. Owns a DOM element.
ComposerStopOptional
Stop button; renders only while generating. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap ComposerInput in Composer and name the input with aria-label or a label.
- 2
Add the supporting parts
Add ComposerSend and, for model replies, ComposerStop.
- 3
Make the behavior clear
Handle onSend with the draft and set generating while the reply streams; stop shows up and sending pauses.
Exampletsx <Composer onSend={send} onStop={stop} generating={generating}> <ComposerInput aria-label="Message" /> <ComposerSend>Send</ComposerSend> <ComposerStop>Stop</ComposerStop> </Composer>;
Keyboard
- ↵
- Sends the draft.
- ⇧↵
- Inserts a new line.
Forms and accessibility
The input submits as the field named message, so the form also works natively.
Accessibility checklist
- Give ComposerInput an accessible name with aria-label or a Label.
- Enter that confirms an IME composition (Japanese, Chinese, Korean) never sends.
- Focus returns to the input after sending or stopping, so the next message can be typed at once.
- Announce when generation starts or ends with a Status or the Messages log, not by moving focus.
API reference
import { Composer, ComposerInput, ComposerSend, ComposerStop } from "@comp0/react";Composer
DOM elementNative form that owns the draft.
| Prop | Type | Description |
|---|---|---|
value | string | Controlled draft. |
defaultValue | string | Initial draft. |
onChange | (value: string) => void | Receives the next draft text. |
onSend | (value: string) => void | Called with a non-empty draft; the draft is cleared afterwards. Native onSubmit still runs first and can veto. |
onStop | () => void | Called when the user stops the reply. |
generating | boolean | A reply is being generated: sending pauses and ComposerStop appears. |
disabled | boolean | Disables the input and sending. |
as | ElementType | Renders another element in place of the form. |
ComposerInput
DOM elementThe textarea; Enter sends unless Shift or Alt is held or an IME composition is active.
ComposerSend
DOM elementSubmit button, disabled while the draft is empty or a reply is generating.
ComposerStop
OptionalDOM elementStop button; renders only while generating.
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
Composer
| Style hook | Meaning |
|---|---|
[data-generating] | A reply is being generated. |