Skip to content
ComposerAttachmentMenu

Attachment

Files in the composer as chips or cards: image thumbnails with a hairline, type icons, sizes, a determinate upload ring, errors with retry, and a 140 ms removal.

Image, uploading, ready, processing and failed chips in a scrolling row.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/attachment.json

Usage

tsx
import { Attachment, AttachmentList } from "@/components/ui/chatcn/attachment"

<AttachmentList>
  {files.map((f) => (
    <Attachment
      key={f.id}
      variant="card"
      name={f.name}
      size={f.size}
      type={f.type}
      previewUrl={f.previewUrl}
      status={f.status}
      progress={f.progress}
      onRemove={() => remove(f.id)}
      onRetry={() => retry(f.id)}
    />
  ))}
</AttachmentList>

onRemove runs after the exit animation, so update your list inside it. AttachmentList scrolls sideways and fades only the edge that has more, in either direction.

Examples

Cards

Square image thumbnails and two-line file cards with progress and errors.

Props

PropTypeNotes
namestringPicks the icon and type label with type
sizenumberBytes, shown as 4.2 KB
typestringMIME type
previewUrlstringImage thumbnail. Cards with one become square tiles
status"uploading" | "processing" | "ready" | "error"
progressnumber0 to 1 while uploading
errorReactNodeSay how to fix it. Default Upload failed
variant"chip" | "card"32 px one line, or 56 px two lines
onRemove, onRetry, onOpen() => voidShow remove, retry and make the name a button

Accessibility

  • The upload ring is a progressbar with aria-valuenow and a percent aria-valuetext.
  • Remove and retry buttons name the file: Remove orders.csv. After a keyboard removal, focus moves to the next chip, or back to the composer.
  • Errors pair red with an icon and words. Remove buttons on cards show on hover and focus, and always on touch.

Motion

  • New attachments enter with opacity and scale from 0.97 over 150 ms.
  • Removal: opacity and scale 0.96 over 140 ms, then the item leaves the list.
  • The ring advances linearly over 200 ms per update, because progress maps to bytes. Reduced motion drops the scale.