Radix UI Primitives

ComposerPrimitive

Primitives for the text input, send button, and attachments.

The user interface to add new messages or edit existing ones.

Dual Use! A Composer placed directly inside a Thread will compose new messages. A Composer placed inside a Message will edit that message.

Anatomy

import { ComposerPrimitive } from "@assistant-ui/react";

// creating a new message
const Composer = () => (
  <ComposerPrimitive.Root>
    <ComposerPrimitive.Quote>
      <ComposerPrimitive.QuoteText />
      <ComposerPrimitive.QuoteDismiss />
    </ComposerPrimitive.Quote>
    <ComposerPrimitive.Attachments />
    <ComposerPrimitive.AddAttachment />
    <ComposerPrimitive.Input />
    <ComposerPrimitive.Send />
  </ComposerPrimitive.Root>
);

// editing an existing message
const EditComposer = () => (
  <ComposerPrimitive.Root>
    <ComposerPrimitive.Input />
    <ComposerPrimitive.Send />
    <ComposerPrimitive.Cancel />
  </ComposerPrimitive.Root>
);

// with voice input (dictation)
const ComposerWithDictation = () => (
  <ComposerPrimitive.Root>
    <ComposerPrimitive.Input />
    <ComposerPrimitive.If dictation={false}>
      <ComposerPrimitive.Dictate />
    </ComposerPrimitive.If>
    <ComposerPrimitive.If dictation>
      <ComposerPrimitive.StopDictation />
    </ComposerPrimitive.If>
    <ComposerPrimitive.Send />
  </ComposerPrimitive.Root>
);

API Reference

Root

Containts all parts of the composer.

This primitive renders a <form> element unless asChild is set.

ComposerRootProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

Input

The text input field for the user to type a new message.

This primitive renders a <textarea> element unless asChild is set.

ComposerPrimitiveInputProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

submitMode: "enter" | "ctrlEnter" | "none"= "enter"

Controls how the Enter key submits messages. "enter": plain Enter submits (Shift+Enter for newline). "ctrlEnter": Ctrl/Cmd+Enter submits (plain Enter for newline). "none": keyboard submission disabled.

Keyboard Shortcuts

Default (submitMode="enter"):

KeyDescription
EnterSends the message.
Shift + EnterInserts a newline.
EscapeSends a cancel action.

With submitMode="ctrlEnter":

KeyDescription
Ctrl/Cmd + EnterSends the message.
EnterInserts a newline.
EscapeSends a cancel action.

Send

The button to send the message.

This primitive renders a <button> element unless asChild is set.

ComposerPrimitiveSendProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

multiple: boolean | undefined= true

Allow selecting multiple attachments at the same time.

Cancel

Sends a cancel action.

In edit composers, this action exits the edit mode. In thread composers, this action stops the current run.

This primitive renders a <button> element unless asChild is set.

ComposerPrimitiveCancelProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

Attachments

Renders attachments. This primitive renders a separate component for each attachment.

ComposerPrimitiveAttachmentsProps
components?: ComposerAttachmentsComponents

The component to render for each attachment.

ComposerPrimitiveAttachmentsProps['components']
Image?: ComponentType

The component to render for each image attachment.

Document?: ComponentType

The component to render for each document attachment.

File?: ComponentType

The component to render for each file attachment.

Fallback?: ComponentType

The component to render for each attachment type.

AttachmentByIndex

Renders a single attachment at the specified index within the composer.

<ComposerPrimitive.AttachmentByIndex
  index={0}
  components={{
    Image: MyImageAttachment,
    Document: MyDocumentAttachment
  }}
/>
ComposerPrimitive.AttachmentByIndex.Props
indexrequired: number

The index of the attachment to render.

components?: ComposerAttachmentsComponents

The components to render for the attachment.

AddAttachment

Renders a button to add an attachment.

This primitive renders a <button> element unless asChild is set.

ComposerPrimitiveAddAttachmentProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

Dictate

Renders a button to start dictation to convert voice to text.

Requires a DictationAdapter to be configured in the runtime.

This primitive renders a <button> element unless asChild is set.

ComposerPrimitiveDictateProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

StopDictation

Renders a button to stop the current dictation session.

Only rendered when dictation is active.

This primitive renders a <button> element unless asChild is set.

ComposerPrimitiveStopDictationProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

DictationTranscript

Renders the current interim (partial) transcript while dictation is active.

Note: By default, interim transcripts are displayed directly in the composer input (like native dictation). This component is for advanced customization when you want to display the interim transcript separately (e.g., in a different style or location).

Only renders when there is an active interim transcript (returns null otherwise).

This primitive renders a <span> element.

{/* Optional: Display interim transcript separately with custom styling */}
<ComposerPrimitive.If dictation>
  <div className="dictation-preview">
    <ComposerPrimitive.DictationTranscript className="italic text-muted" />
  </div>
</ComposerPrimitive.If>

Quote

A container for displaying a quote preview in the composer. Only renders when a quote is set via composer.setQuote().

This primitive renders a <div> element.

<ComposerPrimitive.Quote className="flex items-start gap-2 bg-muted/60 px-3 py-2">
  <ComposerPrimitive.QuoteText className="line-clamp-2 flex-1 text-sm" />
  <ComposerPrimitive.QuoteDismiss>×</ComposerPrimitive.QuoteDismiss>
</ComposerPrimitive.Quote>

QuoteText

Renders the quoted text content. Only renders when a quote is set.

This primitive renders a <span> element.

QuoteDismiss

A button that clears the current quote from the composer by calling setQuote(undefined).

This primitive renders a <button> element unless asChild is set.

ComposerPrimitiveQuoteDismissProps
asChild: boolean= false

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

See the Quoting guide for a complete walkthrough including the floating selection toolbar and backend handling.

If

Renders children if a condition is met.

UseComposerIfProps
editing?: boolean | undefined

Render children if the message is being edited.

dictation?: boolean | undefined

Render children if dictation is active.

<Composer.If editing>{/* rendered if message is being edited */}</Composer.If>

<Composer.If dictation>{/* rendered if dictation is active */}</Composer.If>