File Attachment
Attachment previews for uploads, with a springy progress ring, a check on finish, cancel, retry and remove.
Preview
Hover a ring to cancel. One upload fails on purpose; press its retry button.
Setup
Install with the shadcn CLI
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
Run in your terminal:
$npm install clsx tailwind-merge framer-motion lucide-react - 2
Copy
lib/utils.tsfrom the Code tab. Skip if you already havecn(). - 3
Copy
styles/tokens.cssfrom the Code tab and import it in your global CSS after Tailwind:@import "./styles/tokens.css"; - 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
Import and render:
Exampleimport { 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
| Prop | Type | Default | Notes |
|---|---|---|---|
items | Attachment[] | — | 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. |
className | string | — | 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