File upload
A complete file selection flow with constraints, progress, and error feedback.
pnpm dlx shadcn@latest add @uiarc/file-uploadPattern · live
- Uploading files with per-file progress, retry, and remove.
- Attachments that must be validated by type and size before upload.
- Uploads that should abort when a file is removed, via the AbortSignal.
- Use file-dropzone when you only need to pick files without upload state.
- Use import-mapper when uploaded data needs column mapping.
Installation
Add File upload with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/file-uploadAdds the block and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
Usage
Use it for documents or media when people need to see upload progress and recover from invalid files.
example.tsx
import { FileUpload } from "@/registry/components/file-upload/file-upload"; export function Attachments() { return ( <FileUpload accept="image/*,.pdf" maxSize={10 * 1024 * 1024} onUpload={async (file, { onProgress, signal }) => { await uploadWithProgress(file, onProgress, signal); }} /> );}Built fromFile pickerProgressFeedback
API reference
2 parts. The first is the root.
FileUpload
A dropzone with a file list that validates type and size, runs your upload per file with progress, and supports retry and remove.
PropTypeDefaultDescription
acceptstring–Comma-separated extensions or MIME types, including wildcards like image/*. Rejected files stay listed with an error.maxSizenumber–Maximum size in bytes. Larger files are listed with an error.multiplebooleantrueAllows several files. When false a new file replaces the old one.disabledbooleanfalseDisables the dropzone and input.labelstring"Upload files"Dropzone heading.descriptionstring"Drop files here or browse from your device."Helper text, linked with aria-describedby.valueFileUploadItem[]–Controlled file list.onChange(files: FileUploadItem[]) => void–Called when files are added or removed.onUpload(file: File, options: { onProgress: (percent: number) => void; signal: AbortSignal }) => Promise<void>–Uploads each valid file. Report 0-100 through onProgress, resolve when done, reject to mark it failed. Removing a file aborts its signal.FileUploadItem
Item type: { id: string; file: File; error?: string }.
No props.
- EnterorSpace
- Opens the file picker from the dropzone.
- Tab
- Moves through the dropzone and each row's retry and remove buttons.
- The dropzone is role=button with aria-disabled and aria-describedby for the helper text.
- An aria-live polite status line announces added, rejected, uploaded, failed, and removed files.
- Retry and remove buttons are labelled with the file name.
- After removing a file, focus moves to the neighbouring remove button or back to the dropzone.
- The dropzone label swaps to Drop to add files while dragging; rows open and close their height on a spring.
- One spring drives the progress bar and the counted percentage, the bar folds away once the file lands, and a check draws in.
- Reduced motion jumps progress and replaces height and blur swaps with fades.
- The dropzone and list fill their container; file names ellipsize on one line.
- On touch devices the dropzone opens the native file picker, since drag and drop is desktop only.
- Uploads run through your onUpload, one call per valid file; there is no concurrency limit, so queue large batches yourself.
- The file list is not virtualized; keep it to a reasonable number of files.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use when files are uploaded with progress. Use file-dropzone when you only need to pick files without upload state.
- Wire onUpload to fetch or XHR and honour the AbortSignal; without onUpload files are only listed and validated.
The full library index for assistants is at /llms.txt.