Composer

Composable message-editor parts for an input, toolbar, attachments, and drag-and-drop feedback.

Preview#

64%
PDFrelease-notes.pdf18 KB
PDFuploading-notes.pdf18 KB64%
PDFfailed-upload.pdf18 KB
PDFdesign-spec.pdf18 KB
PDFresearch-notes.pdf18 KB
PDFapi-contract.pdf18 KB
Controls
Attachments
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>
64%
PDFrelease-notes.pdf18 KB
PDFfailed-upload.pdf18 KB

Attachment count limits are product policy. Keep the Banner outside Composer and show it when the consumer rejects another file.

PDFrelease-notes.pdf18 KB
PDFdesign-spec.pdf18 KB
PDFresearch-notes.pdf18 KB
PDFapi-contract.pdf18 KB
PDFattachment-5.pdf18 KB
PDFattachment-6.pdf18 KB
PDFattachment-7.pdf18 KB
PDFattachment-8.pdf18 KB
PDFattachment-9.pdf18 KB
PDFattachment-10.pdf18 KB

Use SortableComposerAttachmentsScroll inside ComposerAttachments for a single-row strip (arrows + horizontal reorder). For wrapping tiles, use SortableComposerAttachments instead.

PDFrelease-notes.pdf18 KB
PDFdesign-spec.pdf18 KB
PDFresearch-notes.pdf18 KB
PDFapi-contract.pdf18 KB
PDFattachment-5.pdf18 KB
PDFattachment-6.pdf18 KB
PDFattachment-7.pdf18 KB
PDFattachment-8.pdf18 KB
PDFattachment-9.pdf18 KB
PDFattachment-10.pdf18 KB

Use SortableComposerAttachments with consumer-owned order. Pass string items / onReorder; render each tile inside SortableComposerAttachment.

PDFrelease-notes.pdf18 KB
PDFdesign-spec.pdf18 KB
PDFresearch-notes.pdf18 KB
PDFapi-contract.pdf18 KB
PDFproduct-brief.pdf18 KB
PDFaudit-log.pdf18 KB
PDFrollout-plan.pdf18 KB
PDFqa-checklist.pdf18 KB

SortableComposerAttachmentsScroll is a thin wrapper around ComposerAttachmentsScrollArea with horizontal sort + x-only autoscroll. Same items / onReorder contract as the wrap variant.

PDFrelease-notes.pdf18 KB
PDFdesign-spec.pdf18 KB
PDFresearch-notes.pdf18 KB
PDFapi-contract.pdf18 KB
PDFproduct-brief.pdf18 KB
PDFaudit-log.pdf18 KB
PDFrollout-plan.pdf18 KB
PDFqa-checklist.pdf18 KB
PDFmeeting-notes.pdf18 KB

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.

Subscribe to send more messages in this channel.

API#WIP

...props
form props
Composition root for autocomplete, pending attachments, and the bordered composer surface.
...props
div props
Bordered composer shell.
...props
textarea props
Auto-growing message textarea.
...props
div props
Toolbar row under the input.
...props
div props
Caller-owned action cluster in the toolbar.
...props
div props
Caller-owned metadata cluster in the toolbar.
...props
button props
Attach-action icon button.
...props
Button props
Accent Button used for submit. Use Button size, loading, and render props; pass visible content as children.
...props
label props
A labelled checkbox or status cluster; the component owns its spacing and typography.
...props
div props
Pending-upload strip above the input.
...props
div props
Opt-in single-row overflow with prev/next arrows. Prefer SortableComposerAttachmentsScroll when reorder is needed — that wrapper includes this shell.
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.
...props
SortableComposerAttachments props
Thin wrapper: ComposerAttachmentsScrollArea + horizontal sort strategy + x-only autoscroll.
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.
...props
div props
Attachment tile within the pending-upload strip.
...props
span props
File-chip tile for non-image uploads.
...props
span props
Fixed 64px image media in its own bordered frame. Place FilePreview media inside.
...props
span props
File-chip text block.
...props
span props
Attachment filename.
...props
span props
Attachment mime/type metadata.
...props
span props
White upload overlay for caller-owned progress content. For images, place as a sibling under ComposerAttachment; for files, nest inside ComposerAttachmentFile.
...props
span props & { value: number }
Bottom linear upload progress bar. Place next to the uploading overlay (image: under ComposerAttachment; file: inside ComposerAttachmentFile).
...props
span props
Borderless failed-upload mask for caller-owned retry feedback.
...props
button props
Corner remove affordance.
...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.
...props
span props
Visible drop feedback content.
...props
span props
Default drop feedback prompt; pass children to replace its visible content.