Chat Attachment
File and media previews for AI chat, plus the picker and drop target that collect them.
Description
ChatAttachment shows one file attached to a prompt or a message. Images and videos with a preview URL render as a square thumbnail with the name on hover. Everything else renders as a card with a FileFormat icon, the name and the size. It is a compound component: the root renders a <div> and picks the layout, and Preview, Icon, Info, Name, Meta and Remove let you rebuild the inside. ChatAttachmentGroup wraps several tiles, and ChatAttachmentInput adds a file picker, a drop target and image paste to a composer.
Use it in a chat composer's attachment row, above a user's message bubble, or inside an assistant reply that returns a file. status="uploading" and status="error" cover the time between picking a file and your server accepting it.
It is not an upload manager. For a standalone upload form with pause, retry and per-file progress bars, use File Upload. For a plain drag and drop area outside a chat surface, use Dropzone. If you only need the format glyph, use File Format.
ChatAttachment is a Create UI Pro component. Install it with a Pro seat.
Installation
Anatomy
Usage
Examples
Every example is a Pro preview (marked with a Pro badge).
Thumbnail or card
The layout follows the data. An image or video with src becomes a thumbnail. Without src there is nothing to preview, so the same file renders as a card. Set mediaType="file" to force the card when you have a URL but want the name visible.
Sizes
size="md" is the 64px composer tile. size="sm" shrinks it to 40px for queued prompts and dense message history. A small thumbnail drops the hover name, since it would not fit.
Upload status
status="uploading" with progress shows a progress ring and the percentage. Leave progress out for a spinner when you have no byte count. status="error" tints the tile and shows error, which defaults to "Upload failed". On a thumbnail the message is announced to screen readers only.
Custom content
Pass children to Icon for a brand glyph or a different FileFormat style, and to Meta to replace the size line. Preview asChild turns your own element, such as a <video> with a poster, into the preview surface.
File input
ChatAttachmentInput owns a hidden file input. Trigger opens it and Dropzone accepts dropped files and pasted images. accept and maxSize apply to every source, and anything filtered out reaches onFilesRejected with a reason.
With Prompt Input
asChild puts the drop target on PromptInput.Shell and the trigger on PromptInput.Action, so the whole composer accepts files without an extra wrapper. The shell styles its own dragging state from data-state.
Accessibility
A tile is not focusable. The only interactive parts are Remove and ChatAttachmentInput.Trigger, which are regular buttons.
ARIA notes:
Removeis labelled"Remove {name}"by default. Passaria-labelwhen the action differs, for example"Cancel upload of {name}".- On a thumbnail,
Removestays hidden until the tile is hovered or holds focus. Touch devices show it at reduced opacity. The hit area is 24px even though the visible chip is 16px. - The image
altand the videoaria-labelcome fromname. The hover name overlay repeats it, so it isaria-hidden. - The
FileFormaticon isaria-hiddenbecause the name is already visible. - Upload progress renders
Progress(role="progressbar") orSpinner(role="status"), labelled"Uploading {name}". Errors are not live regions, so announce failures from your ownaria-liveregion if the user needs to hear them right away. - The hidden file input is
aria-hiddenand out of the tab order. Give an icon-onlyTriggeranaria-label.
Styling
Tailwind override: pass className to any part. Classes merge with the component's CVA classes via cn().
Data slots and attributes: the component sets these for CSS targeting:
data-slot="chat-attachment"on the root, withdata-layout("media"or"file"),data-media-type("image","video","file"),data-size("sm","md") anddata-status("idle","uploading","error").data-slot="chat-attachment-preview"on the preview surface.data-slot="chat-attachment-preview-image"anddata-slot="chat-attachment-preview-video"on the default thumbnail element.data-slot="chat-attachment-status"on the progress or error overlay drawn over a thumbnail.data-slot="chat-attachment-icon"on the icon wrapper, withdata-format(for example"PDF").data-slot="chat-attachment-info",data-slot="chat-attachment-name"anddata-slot="chat-attachment-meta"on the text parts.data-slot="chat-attachment-remove"on the remove button.data-slot="chat-attachment-group"on the group.data-slot="chat-attachment-input-field"on the hidden<input type="file">.data-slot="chat-attachment-input-trigger"on the trigger.data-slot="chat-attachment-input-dropzone"on the drop target, withdata-state("idle","dragging","disabled").
The default dropzone reserves a transparent dashed border, so the data-state="dragging" highlight never shifts layout. With asChild the target keeps its own classes and should style data-state="dragging" itself, as PromptInput.Shell does.
Related Components
- File Upload: full upload list with pause, retry and progress bars, outside a chat composer.
- Dropzone: standalone drag and drop area with its own title, hint and button.
- File Format: the file-type glyph on its own, for tables and file trees.
- Prompt Input: the chat composer these tiles and the input usually sit in.
API Reference
ChatAttachment
The tile. Extends React.ComponentProps<"div">. Also exported as ChatAttachmentRoot and ChatAttachment.Root. Without children it renders Preview and Info.
Props
Variants
The media or file layout is derived from mediaType and src and exposed as data-layout. It is not a prop.
ChatAttachment.Preview
The thumbnail surface, or the icon slot on a card. Extends React.ComponentProps<"div">. Without children it renders the image, the video or Icon. On a thumbnail it also draws the upload overlay, unless asChild is set.
Props
ChatAttachment.Icon
The icon wrapper on a card. Extends React.ComponentProps<"span">. Defaults to a FileFormat for the inferred format, 40px at md and 28px at sm. A plain icon passed as a child is sized to 20px.
Props
ChatAttachment.Info
The text column on a card, or the hover overlay on a thumbnail. Extends React.ComponentProps<"div">. Defaults to Name plus Meta on a card and Name alone on a thumbnail.
Props
ChatAttachment.Name
The truncated file name. Extends React.ComponentProps<"span">.
Props
ChatAttachment.Meta
The secondary line. Extends React.ComponentProps<"div">. Shows the formatted size when idle, the progress ring and percentage while uploading, and the error icon and message on error. Renders nothing when there is no content.
Props
Variants
ChatAttachment.Remove
The remove button. Extends the CloseButton props with appearance="ghost". On a thumbnail it floats in the top end corner and appears on hover or focus.
Props
ChatAttachmentGroup
A wrapping row of tiles. Extends React.ComponentProps<"div">.
Props
ChatAttachmentInput
Provider that renders the hidden <input type="file"> and no wrapper element. Also exported as ChatAttachmentInputRoot and ChatAttachmentInput.Root.
Props
ChatAttachmentInput.Trigger
Opens the file picker. Extends React.ComponentProps<"button">. Calling event.preventDefault() in onClick keeps the picker closed.
Props
ChatAttachmentInput.Dropzone
Accepts dropped files and pasted images in the capture phase, so a textarea inside still receives its own events. Extends React.ComponentProps<"div">.
Props
Helpers
inferChatAttachmentMediaType(mimeType?, name?) returns "image", "video" or "file". inferChatAttachmentFormat(mimeType?, name?) returns the FileFormat label (extension, then MIME type, then "FILE"). formatChatAttachmentSize(bytes?) returns the size string or undefined. chatAttachmentVariants, chatAttachmentGroupVariants and chatAttachmentDropzoneVariants are the CVA recipes.


