Attachment
betaFile and image attachment chips with thumbnails, upload progress rings, error/retry, remove, a “+N” overflow stack and a dropzone.
A single file
minimal
Mixed states with overflow
Thumbnails, a live upload, a failed upload with retry, remove buttons and a +N stack.
full
Hover a file to remove it · Retry the failed upload · Open the +N stack
Dropzone feeding a list
Browse or drop files; accepted ones upload, rejected ones are reported.
composition
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/attachment.jsonCopies the source into components/kura/attachment with its dependencies (kura-theme, kura-motion, use-reduced-motion, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Show files the user attached to a prompt before sending (inside or above a composer).
- Display upload progress, failures and a retry action for files sent to a model.
- Render the files that belong to a sent message or an agent artifact list.
- A standalone drop area for documents to index, e.g. “Add files to this project”.
Reach for something else when
- A complete prompt input with paste and drag-drop built in — use composer (it renders attachment for you).
- Rich previews of generated documents or code — use artifact-card or code-block.
- Audio recordings with playback — use audio-waveform with `peaks`.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name* | string | — | File name shown on the chip; long names truncate with an ellipsis. |
| size | number | — | Size in bytes, rendered as “2.4 MB”. |
| type | string | — | MIME type. Chooses the file-type icon (image, pdf, code, text, sheet, audio, video, archive) and whether a preview renders as a thumbnail. |
| previewUrl | string | — | Image URL (object URL or remote). With an image type the attachment renders as a 44px square thumbnail. |
| status | "ready" | "uploading" | "error" | "ready" | Upload state. Uploading shows a progress ring; error shows a danger tint. |
| progress | number | — | Upload progress 0..1 while uploading. Omit for an indeterminate spinning arc. |
| error | string | — | Error message for status=error; shown as the title and announced via role=alert. |
| variant | "auto" | "chip" | "tile" | "auto" | auto = thumbnail tile for images that have a previewUrl, a chip (icon + name + size) otherwise. |
| onRemove | () => void | — | Render a × button (revealed on hover/focus, always visible on touch). |
| onRetry | () => void | — | Render a Retry action while status is error. |
All native attributes of the root element, className and ref pass through.
Anatomy
- AttachmentOne file. Exposes data-variant (chip|tile), data-kind and data-status (ready|uploading|error).
data-slot="attachment" - AttachmentRemoveThe × button, labelled “Remove <name>”. Rendered when onRemove is set.
data-slot="attachment-remove" - AttachmentListWrapping <ul> of attachments with pop-in/out and gap-closing layout motion, plus the “+N” stack.
data-slot="attachment-list" - AttachmentOverflowThe “+N” stack button (a counter with two offset cards behind it).
data-slot="attachment-overflow" - AttachmentDropzoneClick-to-browse + drop target with a dashed hairline. data-dragging is set only while a drag carries files.
data-slot="attachment-dropzone" - AttachmentProgressRingStandalone circular progress (currentColor) used by uploading attachments.
data-slot="attachment-progress"
Accessibility
- Image tiles render <img alt={name}>; chips render the name as text, so every file has a readable name.
- The remove button is a real <button> labelled “Remove <file name>” with a 28px hit area; it is revealed on hover and on keyboard focus within the item.
- Upload failures are announced once with role=alert; progress is not announced on every tick.
- The list is a <ul aria-label="Attachments">; the overflow button is labelled “Show N more attachments”.
- The dropzone is a native <button> that opens the file picker; the drop target never steals focus.
- Reduced motion: Items fade in and out without scale, blur or layout travel; the indeterminate ring pulses instead of spinning; hover lifts are removed.
| Keys | Action |
|---|---|
| Tab | Moves through each attachment's Retry and Remove buttons |
| Enter / Space | Activates Remove, Retry, the +N stack or the dropzone |
Notes for agents
- Keep attachments in state as AttachmentItem[] and render <AttachmentList items={…} />; use createAttachment(file) to build items (it creates an object URL for images — revoke it with URL.revokeObjectURL when removing).
- Helpers exported alongside: formatBytes, attachmentKind, acceptsFile, filesFromDataTransfer, dragHasFiles.
- Progress is 0..1, not a percentage. Omit progress for an indeterminate ring.
- Inside a composer, prefer composer's built-in ComposerAttachments, which wires paste, drop and removal.