File Upload
A drop zone and a file list for uploads: drag or browse, with progress, success and errors per file.
When to use
- Upload one or more files, such as lab results, scans or a CSV of records, by dragging or browsing.
When not to use
- Pasting text or a URL: use Input or Textarea.
- A single inline file field in a dense form: use Input with
type="file".
Install
npx shadcn@latest add @clinia/file-upload- lab-results-2025-06.pdf471 KBUploaded
- intake-form-scan.png45%
Anatomy
- lab-results-2026-09.pdf45%
- 1Drop zone
data-slot="file-upload"border-dashed · border-input - 2File, with its status
data-slot="file-upload-item"border-border · bg-card
Clinia rules
- The drop zone is a dashed
inputborder at rest. On drag-over it turns solid Trust over abg-accent/15tint. - Say what's accepted in
description: the types and the size limit (PDF or image, up to 5 MB each.). Set the same limits withacceptandmaxSize. - Each rejected file gets a row with the reason (
Larger than 5.0 MB.). Never drop a file silently. - Uploading shows a percentage and an
xsProgress bar; done showsUploadedwith a check; an error shows the reason and a Retry button when you passonRetry.
Do and don't
- intake-scan.tiff8.0 MBLarger than 5 MB.
- intake-scan.tiff8.0 MBSomething went wrong.
States
- intake-form.png45%
- lab-results.pdf471 KBUploaded
- scan.tiff9.0 MBLarger than 5.0 MB.
Focus cells apply the component's focus-visible classes statically. Toggle the site theme to review both themes.
Accessibility
Built in
- The drop zone is a label around a real
<input type="file">, so Tab reaches it and Enter or Space opens the picker. Screen readers get the native control. - Each file's status is written out (a percentage, "Uploaded", or the error) with an icon, never shown by colour alone. Errors are announced with
role="alert". - Drag-over switches the dashed border to solid Trust, at least 3:1 in both themes, the same cue as other selected states.
- The component validates
acceptandmaxSizeand hands you the accepted and rejected files. Sending them is up to your app.
Your responsibility
- If the upload is required or scoped, say so above the drop zone in a
Fieldlabel, such asLab results. - Pass
onRetryfor errors that a second try can fix, such as a network failure. Leave it out when the file itself is the problem. - Move focus somewhere sensible after a file is removed, such as the next file's remove button or the drop zone.
How to test
- Press Tab to reach the drop zone: the Trust ring appears around it. Press Enter or Space: the file picker opens.
- Pick a file that's too large: a row appears with the reason, and a screen reader announces it.
- Tab to a file's remove button: it's announced as
Removeplus the file name. - With a screen reader, an uploading file announces its name and progress.
API
FileUpload
| Prop | Values | Default | Description |
|---|---|---|---|
| accept | string | – | File types the picker offers and the drop zone accepts, in the native |
| disabled | boolean | – | Turns off the picker and the drop zone. |
| multiple | boolean | – | Takes every file picked or dropped. Without it, only the first file is kept. |
| maxSize | number | – | Largest accepted file, in bytes. Larger files come back in |
| onFiles | ((accepted: File[], rejected: FileRejection[]) => void) | – | Accepted files first, then the ones that failed |
| description | React.ReactNode | – | Hint under the prompt: accepted types and the size limit. |
FileUploadItem
| Prop | Values | Default | Description |
|---|---|---|---|
| name | string | – | The file name, shown truncated and used in the remove and retry labels. |
| size | number | – | In bytes; shown as B, KB or MB. |
| status | uploadingdoneerror | – | Each status shows its own text and icon: a percentage, "Uploaded", or the error. |
| progress | number | 0 | 0 to 100 while uploading. |
| error | string | – | The reason shown when |
| onRemove | (() => void) | – | Shows a remove button, labelled with the file name. |
| onRetry | (() => void) | – | Shows a retry button when |
All other props come from the native input element.