Attachment
AIA file chip for the composer and for messages, with upload, processing, error and done states.
npx prodular-ui add attachmentStates
Uploading and processing shimmer the title. Errors say why in the description.
import { FileSpreadsheetIcon, FileTextIcon, RotateCcwIcon, XIcon } from "lucide-react"
import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle,} from "@/components/ui/attachment"import { Spinner } from "@/components/ui/spinner"
export default function AttachmentStates() { return ( <div className="flex w-full max-w-xl flex-col gap-3"> <AttachmentGroup className="flex-wrap"> <Attachment state="done"> <AttachmentMedia> <FileSpreadsheetIcon /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>vendor-invoices-q3.csv</AttachmentTitle> <AttachmentDescription>CSV, 214 KB</AttachmentDescription> </AttachmentContent> <AttachmentActions> <AttachmentAction aria-label="Remove vendor-invoices-q3.csv"> <XIcon /> </AttachmentAction> </AttachmentActions> </Attachment> <Attachment state="uploading"> <AttachmentMedia> <Spinner /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>Northbeam MSA 2026.pdf</AttachmentTitle> <AttachmentDescription>Uploading, 64%</AttachmentDescription> </AttachmentContent> </Attachment> <Attachment state="processing"> <AttachmentMedia> <FileTextIcon /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>Board pack, September.pdf</AttachmentTitle> <AttachmentDescription>Reading 42 pages</AttachmentDescription> </AttachmentContent> </Attachment> <Attachment state="error"> <AttachmentMedia> <FileTextIcon /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>bank-statement-sep.ofx</AttachmentTitle> <AttachmentDescription>Format not supported</AttachmentDescription> </AttachmentContent> <AttachmentActions> <AttachmentAction aria-label="Retry upload"> <RotateCcwIcon /> </AttachmentAction> </AttachmentActions> </Attachment> </AttachmentGroup> </div> )}Sizes and orientation
sm inside the composer, default in messages, vertical for image grids.
import { FileSpreadsheetIcon, ImageIcon } from "lucide-react"
import { Attachment, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger,} from "@/components/ui/attachment"
export default function AttachmentSizes() { return ( <div className="flex w-full max-w-xl flex-col gap-6"> <AttachmentGroup className="flex-wrap items-center"> {(["default", "sm", "xs"] as const).map((size) => ( <Attachment key={size} size={size}> <AttachmentMedia> <FileSpreadsheetIcon /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>payroll-oct.xlsx</AttachmentTitle> {size !== "xs" && <AttachmentDescription>XLSX, 88 KB</AttachmentDescription>} </AttachmentContent> </Attachment> ))} </AttachmentGroup> <AttachmentGroup> {["receipt-uber-0921.png", "receipt-hotel-0922.png", "receipt-dinner-0922.png"].map((name) => ( <Attachment key={name} orientation="vertical"> <AttachmentTrigger aria-label={`Open ${name}`} /> <AttachmentMedia> <ImageIcon /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>{name}</AttachmentTitle> <AttachmentDescription>PNG</AttachmentDescription> </AttachmentContent> </Attachment> ))} </AttachmentGroup> </div> )}Anatomy
<Attachment state="done" size="sm"> <AttachmentMedia><FileTextIcon /></AttachmentMedia> <AttachmentContent> <AttachmentTitle>invoice.pdf</AttachmentTitle> <AttachmentDescription>PDF, 1.2 MB</AttachmentDescription> </AttachmentContent></Attachment>Guidance
Use it when
- In PromptInputAttachments for files the user is about to send, and in a user Message for files they sent.
- processing while the model reads the file, so people know it is not ready yet.
Avoid
- Showing full file paths. Show the file name and a short description like type and size.
- Removing failed files silently. Keep them with state error and a retry action.
Rules for AI agents
Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.
- Always set state: uploading while bytes transfer, processing while the file is parsed or indexed, error on failure, done when it is ready.
- Use size="sm" inside PromptInput and the default size in messages.
- Every AttachmentAction is icon only and needs aria-label naming the file.
- Use AttachmentTrigger when the whole chip opens a preview. It covers the chip and keeps actions clickable.
API
Attachment
| Prop | Type | Default | Description |
|---|---|---|---|
| state | "idle" | "uploading" | "processing" | "error" | "done" | "done" | Upload lifecycle. Exposed as data-state. |
| size | "default" | "sm" | "xs" | "default" | Chip size. |
| orientation | "horizontal" | "vertical" | "horizontal" | Vertical for image grids. |
AttachmentMedia
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "icon" | "image" | "icon" | Image fills the square with an img. |
Installation
The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.
npx prodular-ui add attachment# or, with the registry in components.jsonnpx shadcn@latest add @prodular/attachment