File Upload
Drop or browse files and follow each upload’s progress.
PDF, JPG or CSV up to 10 MB.
Project-brief.pdf
1.2 MBComplete
100%Dashboard-draft.jpg
720 KBUploading…
40%Q3-export.csv
310 KBConnection lost
This example installs with a Siteplane UI Pro license key.
Get a license on the Pro page.
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-upload-demoThis Pattern is part of Siteplane UI Pro, and its source comes with a Pro license. Its previews are free to view and try.
Installation
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-uploadUsage
import {
FileUpload,
type UploadFile,
} from "@/siteplane/components/file-upload";
const files: UploadFile[] = [
{ id: "brief", name: "Brief.pdf", sizeLabel: "1.2 MB", status: "complete" },
];
<FileUpload
accept="application/pdf,image/*"
description="PDF or image, up to 10 MB."
files={files}
maxSize={10 * 1024 * 1024}
onFilesSelected={(selected) => {
// Start your own uploads, then update `files` as they progress.
console.log(selected);
}}
/>;Composition
FileUpload renders every part itself. You supply the files, their status and the handlers.
FileUpload
├── Dropzone (Card)
│ ├── Icon
│ ├── Browse button and hidden file input
│ └── Description
├── Rejection messages (live region)
└── File list
└── Row (Card)
├── FileTypeIcon
├── Name, size and status
├── Progress
├── Try again (failed rows)
└── Remove buttonProgress
Each row shows a progress bar by default. Set variant="fill" to show progress as a tint that fills the row from its start edge, with the percentage in the status line; the tint fades out once the upload completes or fails. The bar stays available to assistive technology in both variants.
Report the real progress of your upload request in files. The examples on this page use a timer instead of a request.
Add files to share with your team.
Annual-report.pdf
4.8 MB62%
62%Team-photo.png
2.1 MB18%
18%Meeting-notes.docx
86 KB100%
100%
This example installs with a Siteplane UI Pro license key.
Get a license on the Pro page.
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-upload-progress-fill-exampleCompact dropzone
Set compact for a shorter dropzone without the icon. It is made for a row where the zone sits beside an avatar or a label. With multiple={false} only the first file of a selection or drop counts, which suits a single profile photo, and the dropzone lists any other file as rejected. This example shows the chosen image as the avatar instead of listing a row.
PNG, JPG or GIF up to 2 MB.
This example installs with a Siteplane UI Pro license key.
Get a license on the Pro page.
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-upload-compact-exampleAccepted files and size limit
accept takes MIME types, wildcards such as image/* and extensions such as .pdf, like a native file input, and also filters the browser’s file dialog. maxSize is a limit per file in bytes. A file that fails either check never reaches onFilesSelected. The dropzone lists it below with the reason, and onRejected receives it. State the limits in description so people see them before they choose files.
Choose a file that is not a PNG or JPG, or larger than 2 MB, to see the messages.
PNG or JPG, up to 2 MB each.
This example installs with a Siteplane UI Pro license key.
Get a license on the Pro page.
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-upload-restrictions-exampleSurface
The dropzone and the rows use the inset surface by default, so they press into the surface that holds them, such as a Card, a Dialog or a Sheet. Set surface="raised" when the upload sits directly on the page background and should lift like a Card. A row whose upload failed keeps its error surface with either value.
Contracts and terms, up to 10 MB.
Service-contract.pdf
640 KBUploading…
55%Terms-of-service.docx
92 KBComplete
100%
This example installs with a Siteplane UI Pro license key.
Get a license on the Pro page.
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-upload-raised-exampleDisabled
Set disabled to switch the upload off, for example for an archived project. The dropzone keeps its surface while its icon and text dim, and its browse button, drops, Try again and remove do nothing. Rows already in the list stay visible.
Uploads are paused while this project is archived.
This example installs with a Siteplane UI Pro license key.
Get a license on the Pro page.
pnpm dlx shadcn@4.18.0 add @siteplane-pro/file-upload-disabled-exampleAPI reference
FileUpload
| Prop | Type | Default | Description |
|---|---|---|---|
files | readonly UploadFile[] | [] | Rows listed under the dropzone, in the order you pass them. The application owns the list, each status and each progress value. |
on | (files: File[]) => void | unset | Called after a selection or a drop with the files that pass accept and maxSize; while multiple is off, only the first file can pass. Start the uploads here. |
on | (files: FileRejection[]) => void | unset | Called with the files that were not taken. A file's reason is type when accept excludes it, size when it is over maxSize and count when it is a surplus file while multiple is off. The dropzone also lists them below itself. |
on | (id: string) => void | unset | Shows a remove button on every row and receives the row’s id. Stop the upload and update files. |
on | (id: string) => void | unset | Shows Try again on a failed row and receives the row’s id. |
accept | string | unset | Comma-separated MIME types, type/* wildcards or .extensions, as for a native file input. |
max | number | unset | Largest accepted file size in bytes. |
multiple | boolean | true | Accept several files at once. When false, only the first file of a selection or drop is taken, and the others are rejected with the reason count. |
disabled | boolean | false | Switches off the dropzone, Try again and remove. |
description | ReactNode | unset | Hint under the dropzone text, such as the allowed types and size. |
variant | bar | fill | bar | Progress as a bar in each row or as a fill behind it. |
compact | boolean | false | Shorter dropzone without the icon, for a row beside an avatar or a label. |
surface | inset | raised | inset | Card surface of the dropzone and the rows. A failed row keeps its error surface. |
labels | Partial<FileUploadLabels> | English text | Every visible and spoken string; see the keys below. |
FileUploadProps extends native div props except onSelect, so className, id and aria-* reach the root. The component is not a form field: it submits nothing and has no name or value. A FileRejection holds the file and its reason.
The keys of labels, each with its English default, are select (Click to upload), drop (or drag and drop), uploading (Uploading…), complete (Complete), failed (Upload failed), retry (Try again), remove (Remove), files (Files, the name of the list), rejectedType (File type not accepted), rejectedSize (File exceeds size limit) and rejectedCount (Only one file can be uploaded). remove and retry are followed by the file name in each button’s accessible name, and each rejection message starts with the file name. With variant="fill", a row shows its percentage in place of uploading and complete.
UploadFile
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | required | Stable key of the row, passed to onRemove and onRetry. |
name | string | required | File name. Its extension chooses the FileTypeIcon, and it names the progress bar. |
size | string | required | Size as text, formatted by the application, such as 720 KB. |
status | uploading | complete | error | required | State of the row. A complete row shows 100%. |
progress | number | 0 | Percent from 0 to 100 while uploading. Values outside the range are clamped. |
error | string | Upload failed | Message of a failed row, in place of labels.failed. |
Usage notes
FileUpload does not upload anything. Start the request in onFilesSelected, report progress and the final status through files, and own cancellation, retries and server-side validation. Removing a row only calls onRemove; abort the request there.
accept and maxSize help the person choosing files. They are not a security check, so validate type and size again on the server.
Give every row a stable id: rows animate in and out by key. Format sizeLabel and error messages for your locale and pass translated strings through labels.
The dropzone is operated through its browse button; the hidden file input is not a tab stop. Rejection messages appear below the dropzone in a polite live region. After a row is removed, focus moves to a neighboring row or back to the browse button, and after Try again it moves to the row’s remove button, or to the browse button when the row has none. With reduced motion, rows, rejection messages and the fill change without animation.
The file rows use File Type Icon.