Composer
Composable message-editor parts for an input, toolbar, attachments, and drag-and-drop feedback.
import {
Checkbox,
Composer,
ComposerActions,
ComposerAttachment,
ComposerAttachmentBody,
ComposerAttachmentFile,
ComposerAttachmentImage,
ComposerAttachmentMeta,
ComposerAttachmentRemove,
ComposerAttachments,
ComposerAttachmentTitle,
ComposerAttachmentUploadingOverlay,
ComposerAttachmentUploadProgressBar,
ComposerDropOverlay,
ComposerDropOverlayPrompt,
ComposerIconButton,
ComposerInput,
ComposerMeta,
ComposerRoot,
ComposerSubmit,
ComposerToolbar,
ComposerToolbarLabel,
SortableComposerAttachment,
SortableComposerAttachmentsScroll,
Spinner,
} from "raft-ui";
<div className="relative">
<ComposerRoot>
<ComposerAttachments>
{/* wrap: SortableComposerAttachments — scroll: SortableComposerAttachmentsScroll */}
<SortableComposerAttachmentsScroll items={["shot", "notes"]} onReorder={() => {}}>
<SortableComposerAttachment id="shot" label="screenshot.png">
<ComposerAttachment>
<ComposerAttachmentImage />
<ComposerAttachmentUploadingOverlay>
<Spinner />
64%
</ComposerAttachmentUploadingOverlay>
<ComposerAttachmentUploadProgressBar value={64} />
<ComposerAttachmentRemove />
</ComposerAttachment>
</SortableComposerAttachment>
<SortableComposerAttachment id="notes" label="release-notes.pdf">
<ComposerAttachment>
<ComposerAttachmentFile>
<ComposerAttachmentBody>
<ComposerAttachmentTitle />
<ComposerAttachmentMeta>18 KB</ComposerAttachmentMeta>
</ComposerAttachmentBody>
<ComposerAttachmentUploadingOverlay>
<Spinner />
64%
</ComposerAttachmentUploadingOverlay>
<ComposerAttachmentUploadProgressBar value={64} />
</ComposerAttachmentFile>
<ComposerAttachmentRemove />
</ComposerAttachment>
</SortableComposerAttachment>
</SortableComposerAttachmentsScroll>
</ComposerAttachments>
<Composer>
<ComposerInput />
<ComposerToolbar>
<ComposerActions>
<ComposerIconButton />
</ComposerActions>
<ComposerMeta>
<ComposerToolbarLabel>
<Checkbox />
As Task
</ComposerToolbarLabel>
<ComposerSubmit />
</ComposerMeta>
</ComposerToolbar>
</Composer>
</ComposerRoot>
<ComposerDropOverlay>
<ComposerDropOverlayPrompt />
</ComposerDropOverlay>
</div>Attachment count limits are product policy. Keep the Banner outside Composer and show it when the consumer rejects another file.
Use SortableComposerAttachmentsScroll inside ComposerAttachments for a single-row strip (arrows + horizontal reorder). For wrapping tiles, use SortableComposerAttachments instead.
Use SortableComposerAttachments with consumer-owned order. Pass string items / onReorder; render each tile inside SortableComposerAttachment.
SortableComposerAttachmentsScroll is a thin wrapper around ComposerAttachmentsScrollArea with horizontal sort + x-only autoscroll. Same items / onReorder contract as the wrap variant.
Composer does not own preview state. Ready image attachments stay clickable tiles; the consumer opens Lightbox and owns gallery index / navigation.
Toolbar slots are consumer-owned. Replace the left actions with policy copy when send is blocked, or with a single custom affordance while keeping submit on the right.
API#WIP
| Prop | Type | Default | Description |
|---|---|---|---|
...props | form props | — | Composition root for autocomplete, pending attachments, and the bordered composer surface. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Bordered composer shell. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | textarea props | — | Auto-growing message textarea. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Toolbar row under the input. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Caller-owned action cluster in the toolbar. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Caller-owned metadata cluster in the toolbar. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | Button props | — | Accent Button used for submit. Use Button size, loading, and render props; pass visible content as children. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | label props | — | A labelled checkbox or status cluster with theme-aware spacing and typography. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Pending-upload strip above the input. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Opt-in single-row overflow with prev/next arrows. Prefer SortableComposerAttachmentsScroll when reorder is needed — that wrapper includes this shell. |
| Prop | Type | Default | Description |
|---|---|---|---|
items | readonly string[] | — | Ordered attachment ids. Consumer owns the list and maps ids back to tile data. |
onReorder | (items: string[]) => void | — | Commits the reordered id list on drag end. Live draft order is kept inside the sortable while dragging. |
children | ReactNode | — | SortableComposerAttachment children for each id in items. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | SortableComposerAttachments props | — | Thin wrapper: ComposerAttachmentsScrollArea + horizontal sort strategy + x-only autoscroll. |
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Stable id matching an entry in the parent items list. |
label | string | — | Accessible name used for the reorder control (e.g. filename). |
children | ReactNode | — | ComposerAttachment tile content. DragOverlay clones this as the pointer ghost. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Attachment tile within the pending-upload strip. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | File-chip tile for non-image uploads. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | Fixed 64px image media. Place FilePreview media inside. The attachment root owns the brutal frame border when this part is present. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | File-chip text block. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | Attachment filename. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | Attachment mime/type metadata. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | White upload overlay for caller-owned progress content. For images, place as a sibling under ComposerAttachment; for files, nest inside ComposerAttachmentFile. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props & { value: number } | — | Bottom linear upload progress bar. Place next to the uploading overlay (image: under ComposerAttachment; file: inside ComposerAttachmentFile). |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | Borderless failed-upload mask for caller-owned retry feedback. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | button props | — | Corner remove affordance. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | div props | — | Drag-and-drop overlay that fills its nearest positioned ancestor. May sit inside ComposerRoot or outside it on a shared relative wrapper. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | Visible drop feedback content. |
| Prop | Type | Default | Description |
|---|---|---|---|
...props | span props | — | Default drop feedback prompt; pass children to replace its visible content. |