File Attachment

Attachment previews for uploads, with a springy progress ring, a check on finish, cancel, retry and remove.

Preview

Setup

Install with the shadcn CLI

$npx shadcn@latest add https://kinetikui.vercel.app/r/file-attachment.json

One command adds every file, the npm packages and any Kinetik components it builds on. Needs a components.json; the CLI offers to create one if your project has none.

Or install manually

  1. 1

    Run in your terminal:

    $npm install clsx tailwind-merge framer-motion lucide-react
  2. 2

    Copy lib/utils.ts from the Code tab. Skip if you already have cn().

  3. 3

    Copy styles/tokens.css from the Code tab and import it in your global CSS after Tailwind: @import "./styles/tokens.css";

  4. 4

    Create a components/ui/file-attachment/ folder and copy every file from the Code tab into it. Each file does one job, so you can read or change a piece without the rest.

  5. 5

    Import and render:

    Example
    import { AttachmentList } from "@/components/ui/file-attachment"
    
    <AttachmentList items={files} onRemove={remove} />

Usage

Images show as thumbnails that sharpen as they upload. Other files show as a chip with a coloured extension tile and a live “1.2 MB of 3.4 MB” line. When an upload finishes the ring pops into a check; when it fails the tile turns red with a retry button.

import { AttachmentList, type Attachment } from "@/components/ui/file-attachment"

const files: Attachment[] = [
  { id: "1", name: "photo.png", size: 2_400_000, type: "image/png", url: previewUrl, progress: 0.4, status: "uploading" },
  { id: "2", name: "report.pdf", size: 1_200_000, progress: 1, status: "done" },
]

<AttachmentList
  items={files}
  onCancel={(id) => abortUpload(id)}
  onRetry={(id) => startUpload(id)}
  onRemove={(id) => removeFile(id)}
/>

You drive progress and status from your upload code. Leave progress undefined for a spinning ring when the size is unknown. ProgressRing and AttachmentPreview are exported on their own too.

Props

PropTypeDefaultNotes
itemsAttachment[]—id, name, size, type, url, progress (0–1), status.
onCancel(id) => void—Ring button while uploading.
onRetry(id) => void—Shown when status is "error".
onRemove(id) => void—Corner button on hover once done or failed.
classNamestring—Classes for the list.

Accessibility

  • Cancel, retry and remove are labelled buttons that name the file
  • A polite status line announces how many files are still uploading
  • Respects prefers-reduced-motion