skrewww

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.

The server could not process these files.

Uploads are unavailable.

Native form submission

Multipart FormData

Submit uses the native file input — not a hidden text field.

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

PropTypeDefaultDescription
namestringNative file input name for multipart submission.
labelstringVisible field label via FormField.
acceptstringNative accept attribute (MIME, wildcard, or extension).
multiplebooleanfalseAllows selecting more than one file.
maxFilesnumberMaximum accepted files in multiple mode.
maxSizenumberMaximum file size in bytes.
requiredbooleanNative required validation on the file input.
disabledbooleanDisables selection, drop, and remove actions.
errorstringConsumer-owned field error via FormField.
onFilesChange(files: File[]) => voidFires once with accepted files after each replacement selection.
onRejectedFiles(rejections) => voidFires 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."
    />
  );
}