Skip to Content
⭐️ Leave a star →
ComponentsFileUpload

File Upload

Headless file upload. Combines a hidden native <input type="file"> with a styleable dropzone or trigger button. Validates against accept, maxFiles, and maxSize and reports rejections via onReject.

Features

Click-to-pick Trigger and drag-and-drop Dropzone — use either or both.
Built-in accept / maxFiles / maxSize validation.
Controlled and uncontrolled file list.
onReject callback reports rejected files with a reason.
Render-prop Items for listing selected files with a per-row remove.
data-dragging on the dropzone for drag-over styling.

Example

Click or drag images here

PNG / JPG · max 2 MB each

    import { FileUpload } from '@wire-ui/react' <FileUpload.Root multiple> <FileUpload.Input /> <FileUpload.Dropzone> Click or drag files here </FileUpload.Dropzone> <FileUpload.Items> {(file, i, remove) => ( <li key={i}> {file.name} <button onClick={remove}>Remove</button> </li> )} </FileUpload.Items> </FileUpload.Root>

    Styling

    The Dropzone exposes data-dragging while the user is dragging files over it, and data-disabled when disabled. Use these to give visual feedback during drag-over.

    <FileUpload.Dropzone className=" border border-dashed border-black data-[dragging]:bg-[#e5e5e5] data-[dragging]:border-solid data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed " />

    Using data attributes

    [data-dragging] { background: #e5e5e5; border-style: solid; } [data-disabled] { opacity: 0.5; cursor: not-allowed; }

    Trigger vs Dropzone

    Both open the file picker. Use Trigger for a button-style flow (“Choose files”) and Dropzone for drag-and-drop with click fallback. They can coexist within the same Root.

    Validation and rejections

    Files that fail accept, maxSize, or maxFiles are passed to onReject with a reason. Accepted files are appended to the list (single-file uploaders replace the existing file).

    <FileUpload.Root accept=".pdf,.doc,.docx" maxFiles={3} maxSize={5 * 1024 * 1024} multiple onReject={(rejected) => { for (const { file, reason } of rejected) { // reason is 'maxFiles' | 'maxSize' | 'accept' console.warn(`${file.name} rejected: ${reason}`) } }} />

    Root props

    PropTypeDefaultDescription
    valueFile[]Controlled file list
    defaultValueFile[][]Initial file list
    onChange(files: File[]) => voidCalled when the list changes
    multiplebooleanfalseAllow selecting multiple files
    acceptstringComma-separated MIME types or extensions (e.g. image/* or .pdf,.doc)
    maxFilesnumberMaximum number of files allowed
    maxSizenumberMaximum size per file in bytes
    disabledbooleanfalseDisables the trigger + dropzone
    onReject(rejected: { file: File; reason: 'maxFiles' | 'maxSize' | 'accept' }[]) => voidCalled when files are rejected

    Items render-prop

    FileUpload.Items takes a single function child: (file, index, remove) => ReactNode. Calling remove() deletes that file from the list.

    Data attributes

    AttributeElementWhen present
    data-draggingDropzoneA drag-over is in progress
    data-disabledRoot, Dropzone, Triggerdisabled prop is true

    Accessibility

    • The native <input type="file"> stays in the DOM (visually hidden) so screen readers and form submission work normally.
    • Dropzone is role="button" with tabIndex={0} so it’s keyboard-accessible.
    • Pressing Enter or Space on the focused Dropzone opens the file picker.

    Keyboard Interactions

    KeyDescription
    TabMoves focus to the Trigger or Dropzone.
    Enter / SpaceOpens the native file picker.
    Last updated on

    MIT License © 2026 wire-ui

    File Upload – Wire UI