Forms
File Upload
File Upload is a native file input with drag-and-drop dropzone, advisory validation, selected-file list, and multipart form submission — selection only, not network upload.
State (Empty/Dragging/Filled/Error/Disabled) — 5 variants
betaReact AvailableFigma AvailableDocs Partial
- React last updated
- 2026-07-13
- Documentation last updated
- 2026-06-01
- Accessibility target
- WCAG 2.2 AA (target)
- Version
- 0.1.0-beta
Known open questions
- Figma component-set node ID unresolved — live MCP verification pending.
- Temporary file-upload tokens await Figma variable confirmation.
- Controlled files prop is intentionally unsupported in this MVP.
- Upload progress, retry, preview, and async behavior are deferred.
Live preview
File Upload selects and validates files. Your application owns the network upload.
Single file
PNG or JPG up to 2 MB.
Multiple files
Replacement selection semantics
Each new picker or drop batch replaces the current selection.
Selected batch: None
States
Required / Error / Disabled
A PDF is required before submit.
Uploads are unavailable.
Native form submission
Multipart FormData
Purpose
A dropzone for uploading one or more files, covering the empty/dragging/filled/error/disabled lifecycle states.
Anatomy
FileUpload = FormField + native file input overlay + dropzone + selected-file list + remove buttons + polite status region.
Variants and states
empty · dragging · filled · error · disabled
When to use
Any file attachment or upload flow (avatar upload, document attachment, bulk import).
When not to use
N/A — this is the standard pattern for file upload.
Accessibility
The dropzone must remain a real, labeled file input under the hood — drag-and-drop alone excludes keyboard and screen-reader users.
Keyboard behavior
Tab reaches the native file input over the dropzone. Enter/Space in the file picker opens the OS chooser. Remove buttons are independently focusable.
Common mistakes
Building drag-and-drop as the only interaction path, with no accessible fallback.
Properties
State as variants. File Name (text, only meaningful on the Filled variant).
What does File Upload own?
File Upload selects, validates, lists, and removes files locally. Your application performs the network upload.
Does File Upload perform the network upload?
No. There are no uploadUrl, autoUpload, or storage props. Consumers read files from onFilesChange, the input, or FormData.
Can File Upload submit through a native form?
Yes. Files submit through the real input[type=file] using native multipart encoding when synchronized via DataTransfer.
How are rejected files reported?
Invalid type, size, or count rejections render as text messages and fire onRejectedFiles once per batch. Valid files in mixed batches are kept.
Why is server-side validation still required?
accept is a picker hint, MIME values can be spoofed, and client size checks are advisory only.
Tokens used
semantic/border/defaultsemantic/action/primarysemantic/action/dangersemantic/surface/elevated
Known limitation
React Beta implemented (2026-07-13) with native multipart submission, drag-and-drop, advisory validation, and selected-file removal. Live Figma MCP verification, progress UI, preview thumbnails, and controlled files remain deferred. See docs/architecture/file-upload-discovery.md.
Component API
| Prop | Type | Default | Description |
|---|---|---|---|
| name | string | — | Native file input name for multipart submission. |
| label | string | — | Visible field label via FormField. |
| accept | string | — | Native accept attribute (MIME, wildcard, or extension). |
| multiple | boolean | false | Allows selecting more than one file. |
| maxFiles | number | — | Maximum accepted files in multiple mode. |
| maxSize | number | — | Maximum file size in bytes. |
| required | boolean | — | Native required validation on the file input. |
| disabled | boolean | — | Disables selection, drop, and remove actions. |
| error | string | — | Consumer-owned field error via FormField. |
| onFilesChange | (files: File[]) => void | — | Fires once with accepted files after each replacement selection. |
| onRejectedFiles | (rejections) => void | — | Fires once with advisory rejection details. |
React example
Copy React example
import { FileUpload } from "@/components/ui/FileUpload";
export function Example() {
return (
<FileUpload
label="Upload documents"
name="documents"
accept="image/png,image/jpeg,.pdf"
multiple
maxFiles={3}
maxSize={5_000_000}
supportingText="PNG, JPG, or PDF up to 5 MB. Your app owns the upload request."
/>
);
}