Attachment

File and image attachment rendering for the composer and messages.

The Attachment primitive renders file and image attachments. It appears in two places: inside the composer for pending uploads (with a remove button), and inside messages for sent attachments (read-only). You provide the layout and styling.

Send a message...

Quick Start

An attachment inside a composer:

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

<ComposerPrimitive.Root>
  <ComposerPrimitive.Attachments>
    {() => <MyAttachment />}
  </ComposerPrimitive.Attachments>
  <ComposerPrimitive.Input placeholder="Ask anything..." />
  <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
</ComposerPrimitive.Root>

Root renders a <div>, unstable_Thumb renders a <div> showing the file extension with a leading dot (e.g., .pdf), Name renders plain text, and Remove renders a <button>.

Runtime setup: primitives require runtime context. Wrap your UI in AssistantRuntimeProvider with a runtime (for example useLocalRuntime(...)). See Pick a Runtime.

Core Concepts

Two Contexts

Attachments appear in two different contexts:

  • Composer: via ComposerPrimitive.Attachments. These are pending uploads that can be removed before sending.
  • Messages: via MessagePrimitive.Attachments. These are sent attachments, displayed read-only (the iterator renders attachments for user messages).

The same AttachmentPrimitive parts work in both contexts. The difference is behavioral: Remove only works in the composer context.

Iterator Pattern

You don't loop over attachments yourself. Instead, use the children render function:

// Composer attachments
<ComposerPrimitive.Attachments>
  {({ attachment }) => {
    if (attachment.type === "image") return <ImageAttachment />;
    if (attachment.type === "document") return <DocumentAttachment />;
    return <GenericAttachment />;
  }}
</ComposerPrimitive.Attachments>

// Message attachments
<MessagePrimitive.Attachments>
  {({ attachment }) => {
    if (attachment.type === "image") return <ImageAttachment />;
    if (attachment.type === "document") return <DocumentAttachment />;
    return <GenericAttachment />;
  }}
</MessagePrimitive.Attachments>

Each component receives its attachment context automatically, with no props to pass. components is deprecated — use the children render function instead.

Remove Button

Remove calls the attachment runtime remove action. In composer attachments it removes the attachment from the pending message. In message attachments, this action is not supported and clicking Remove will throw "Message attachments cannot be removed". Only render Remove in composer UIs.

Name as Text

Name renders plain text with no wrapper element. Wrap it in a <span> or other element for styling:

<span className="text-sm font-medium">
  <AttachmentPrimitive.Name />
</span>

Parts

Root

Container for a single attachment item. Renders a <div> element unless asChild is set.

<AttachmentPrimitive.Root className="flex items-center gap-2 rounded-lg border p-2">
  <AttachmentPrimitive.Name />
</AttachmentPrimitive.Root>

unstable_Thumb

Thumbnail slot for attachment previews. Renders a <div> element unless asChild is set.

<AttachmentPrimitive.unstable_Thumb className="flex size-10 items-center justify-center rounded bg-muted text-xs" />

Name

Renders the attachment filename text.

<AttachmentPrimitive.Name />

Remove

Button that removes the current attachment when used in a removable attachment context. Renders a <button> element unless asChild is set.

<AttachmentPrimitive.Remove className="rounded-full p-1 hover:bg-muted">
  <XIcon className="size-3" />
</AttachmentPrimitive.Remove>

Patterns

Composer Attachment with Remove

function ComposerAttachmentItem() {
  return (
    <AttachmentPrimitive.Root className="flex items-center gap-2 rounded-lg border p-2">
      <AttachmentPrimitive.unstable_Thumb className="flex size-10 items-center justify-center rounded bg-muted text-xs font-mono" />
      <span className="min-w-0 flex-1 truncate text-sm">
        <AttachmentPrimitive.Name />
      </span>
      <AttachmentPrimitive.Remove className="rounded-full p-1 hover:bg-muted">
        <XIcon className="size-3" />
      </AttachmentPrimitive.Remove>
    </AttachmentPrimitive.Root>
  );
}

<ComposerPrimitive.Root>
  <ComposerPrimitive.Attachments
    components={{ Attachment: ComposerAttachmentItem }}
  />
  <ComposerPrimitive.Input placeholder="Ask anything..." />
  <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
</ComposerPrimitive.Root>

Message Attachment (Read-Only)

function MessageAttachment() {
  return (
    <AttachmentPrimitive.Root className="flex items-center gap-2 rounded-lg border p-2">
      <AttachmentPrimitive.unstable_Thumb className="flex size-10 items-center justify-center rounded bg-muted text-xs font-mono" />
      <span className="text-sm">
        <AttachmentPrimitive.Name />
      </span>
    </AttachmentPrimitive.Root>
  );
}

Custom Layout with asChild

<AttachmentPrimitive.Root asChild>
  <li className="flex items-center gap-3 py-1">
    <AttachmentPrimitive.unstable_Thumb className="size-8 rounded bg-muted text-xs" />
    <span className="text-sm">
      <AttachmentPrimitive.Name />
    </span>
  </li>
</AttachmentPrimitive.Root>

Relationship to Components

The shadcn Attachment component wraps these primitives with styled thumbnails, tooltip filenames, remove buttons, and image preview dialogs. Start there for a prebuilt attachment UI.

API Reference

For full prop details on every part, see the AttachmentPrimitive API Reference.

Related: