Attachments
The file-attachment tray — item chips, a remove affordance, and inline or global drop zones.
Usage guidelines
- Pending-file tray — the strip above the composer input, showing attachment chips with a remove affordance.
- Model included — accept matching, blob-URL lifecycle, and send preparation ship in the package; the styled tray animates items in and out.
- Media-aware — each item exposes
data-media-type(image/pdf/file) for per-type styling. - Drop + pick — a
Dropzoneoverlay (inline, orglobalto project into the app shell) plus aTriggerfile picker. - Get started — see Installation to add the package and copy the component.
Anatomy
<Attachments show={items.length > 0}>
{items.map((item) => (
<Attachments.Item key={item.id} item={item}>
<Attachments.Remove onRemove={() => remove(item.id)} />
</Attachments.Item>
))}
</Attachments>With a drop zone and a picker trigger:
<>
<Attachments.Dropzone visible={isDragging} variant="global" />
<Attachments show={items.length > 0}>
{items.map((item) => (
<Attachments.Item key={item.id} item={item}>
<Attachments.Remove onRemove={() => remove(item.id)} />
</Attachments.Item>
))}
</Attachments>
<Attachments.Trigger onClick={openFileDialog} />
</>Accessibility
Attachments.Remove and Attachments.Trigger are real buttons with default
overridable names ("Remove attachment" / "Add attachment"). Pass filename to
Remove so each chip announces distinctly ("Remove report.pdf") — without it,
N remove buttons all read the same. Attachments.Error is a role="alert"
live region: content appearing inside it announces immediately (the copy is
still yours).
API reference
Every part accepts className, style, and render (see
PrimitiveProps) and emits a bespoke part attribute (data-<part>) unless noted.
Attachments
The tray container; animates open/closed. Renders data-attachments.
| Prop | Type | Default |
|---|---|---|
show | boolean | true |
Attachments.Item
One attachment chip. Renders data-attachments-item.
| Prop | Type | Default |
|---|---|---|
item | AttachmentItem | (required) |
| Attribute | Values | Description |
|---|---|---|
data-attachments-item | — | The chip. |
data-media-type | "image" | "pdf" | "file" | The item's media category, for per-type styling. |
Attachments.Remove
Removal affordance, shown on hover. Renders <button data-attachments-remove>
named "Remove attachment", or Remove {filename} when filename is set.
| Prop | Type | Default |
|---|---|---|
onRemove | () => void | (required) |
filename | string | — |
Attachments.Dropzone
Drop overlay. Renders data-attachments-dropzone. variant="global"
portals it into the app shell so files can be dropped anywhere.
| Prop | Type | Default |
|---|---|---|
visible | boolean | false |
variant | "inline" | "global" | — |
keepMounted | boolean | false |
portalSelector | string | — |
| Attribute | Description |
|---|---|
data-attachments-dropzone | The overlay. |
data-visible | Present while visible is true. |
Attachments.Error
Validation message slot. Renders <span role="alert" data-attachments-error>,
so content appearing inside it announces immediately. Validation emits a
structured AttachmentErrorCode ("accept", "max_file_size", "max_files")
on the composer's attachments state — you map codes to your own (localized)
copy; the package ships no messages.
Attachments.Trigger
The file-picker button. Renders <button data-attachments-trigger> named
"Add attachment" by default.
Utilities
The wrapper re-exports the attachment surface so you import everything from one
place. From the package (generic mechanics): toAttachmentItem (the default
blob ingestion), matchesAccept, and revokeAttachmentUrl. From the app
(policy, taxonomy, send strategy): the ATTACHMENT_* accept/limit policy,
isImageAttachment / isPdfAttachment, formatFileSize, and
prepareAttachmentsForSend — the adapter that turns submitted items into AI
SDK file parts. The package imposes none of these; they're yours to change.