Skip to content

Pro pre-sale is open. Installation opens January 2027. See Pro

Components

FileUpload

Pick files with a dropzone: drag them in, or click or press Enter on it to open the file picker. Files that are too large or of the wrong type are turned down with a reason. Picked files list below with their upload progress, an uploaded mark or the error with a retry button, and a remove button named with each file.

  • npm: 2 direct, 4 total
  • registry: 5 direct, 5 total

Preview

PNG, JPG or PDF, up to 25 MB
  • brief.pdf482 KB
    Uploaded
  • site-photo.jpg
  • walkthrough.movThe connection dropped

Props

  • multiple
  • disabled
  • invalid
  • size
  • files
  • limits
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload hint="PNG, JPG or PDF, up to 25 MB" defaultValue={sampleUploads} onRetry={retryUpload} />

Usage

When to use

  • Let people add files to a form, a ticket or a project, by dragging them in or picking them.
  • Show each file's upload progress, and let a failed upload be retried or removed.

When not to use

  • Attach files to a chat message. Use Composer with AttachmentPreview.
  • Show files already sent, as links. Use AttachmentPreview with href.
  • Pick one avatar that should be seen large. Show the image with a change Button.

Variants

size

Dropzone padding, icon and text: sm for a side panel, lg for a page that is mostly the upload.

  • PNG, JPG or PDF, up to 25 MB
    • brief.pdf482 KB
      Uploaded
    • site-photo.jpg
    • walkthrough.movThe connection dropped

    sm

  • PNG, JPG or PDF, up to 25 MB
    • brief.pdf482 KB
      Uploaded
    • site-photo.jpg
    • walkthrough.movThe connection dropped

    mddefault

  • PNG, JPG or PDF, up to 25 MB
    • brief.pdf482 KB
      Uploaded
    • site-photo.jpg
    • walkthrough.movThe connection dropped

    lg

import { FileUpload } from "@/components/ui/file-upload"

<FileUpload size="sm" hint="PNG, JPG or PDF, up to 25 MB" defaultValue={sampleUploads} onRetry={retryUpload} />
<FileUpload hint="PNG, JPG or PDF, up to 25 MB" defaultValue={sampleUploads} onRetry={retryUpload} />
<FileUpload size="lg" hint="PNG, JPG or PDF, up to 25 MB" defaultValue={sampleUploads} onRetry={retryUpload} />

Examples

Images

accept and maxSize turn down other files with a reason, and hint says the rules up front.

PNG, JPG or GIF, up to 5 MB
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload multiple accept="image/*" maxSize={5000000} hint="PNG, JPG or GIF, up to 5 MB" onFilesAdd={uploadFiles} />

Upload progress

Each file shows its progress, an uploaded mark, or its error with a retry button.

Up to 25 MB each
  • brief.pdf482 KB
    Uploaded
  • site-photo.jpg
  • walkthrough.movThe connection dropped
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload multiple defaultValue={sampleUploads} onRetry={retryUpload} hint="Up to 25 MB each" />

With a label

Name the field with Label and inputId. maxFiles 1 keeps one file.

PDF or Word, up to 10 MB
import { FileUpload } from "@/components/ui/file-upload"
import { Label } from "@/components/ui/label"

<div className="flex w-full flex-col gap-xs">
  <Label htmlFor="resume">Resume</Label>
  <FileUpload inputId="resume" accept=".pdf,.doc,.docx" maxFiles={1} hint="PDF or Word, up to 10 MB" maxSize={10000000} />
</div>

Invalid

invalid turns the edge to danger, and error says why under the dropzone.

PDF or image

Add at least one receipt

import { FileUpload } from "@/components/ui/file-upload"

<FileUpload invalid error="Add at least one receipt" hint="PDF or image" accept="application/pdf,image/*" />

Disabled

Uploads open after the review
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload disabled hint="Uploads open after the review" />

In a side panel

size sm keeps the dropzone short.

Up to 25 MB each
  • meeting-notes.md6.4 KB
    Uploaded
  • assets.zip12 MB
    Uploaded
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload size="sm" multiple defaultValue={sampleUploadsDone} hint="Up to 25 MB each" />

Do and don't

Do

  • Say the accepted types and the size limit in hint, so nobody finds out by failing.
  • Start uploads in onFilesAdd and show each file's progress in value.
  • Keep failed files in the list with the reason, and pass onRetry when a retry can work.

Don't

  • Do not hide the input or replace it with a div; the real input gives the picker, the keyboard and forms for free.
  • Do not remove a failed file on its own; let the person remove it.
  • Do not use it for one avatar or cover photo that should be seen large; show the image and a change button instead.

Props

NameTypeValuesDefaultDescription
valueFileUploadItem[]--The files, controlled. Each item has id, name, type, size, src, status (uploading, done or error), progress and error.
defaultValueFileUploadItem[]--The first files, uncontrolled.
onValueChange(items: FileUploadItem[]) => void--Called with the whole list after files are added or removed.
onFilesAdd(items: FileUploadItem[]) => void--Called with the accepted new files, to start their uploads.
onRemove(item: FileUploadItem) => void--Called when a remove button is pressed, before the list changes, to cancel an upload.
onRetry(item: FileUploadItem) => void--Shows a retry button on failed uploads and is called when it is pressed.
acceptstring--File types, as on the input, such as image/*,.pdf. Dropped files of other types are turned down.
maxSizenumber--The largest file in bytes. Larger files are listed with an error.
maxFilesnumber--The most files in the list. Files over it are not added, and a notice says so.
multipleboolean-falseAllows more than one file. Without it a new file takes the place of the old one.
disabledboolean-falseTurns off the dropzone and the buttons.
invalidboolean-falseMarks the field invalid, with a danger edge. Pair it with error.
errorReact.ReactNode--Why the field is invalid, shown under the dropzone and read with the input.
hintReact.ReactNode--A line under the prompt, such as the types and the size limit.
sizeenumsm | md | lgmdDropzone padding, icon and text: sm for a side panel, lg for a page that is mostly the upload.
namestring--The input's name, for a form post.
inputIdstring--The input's id, for a Label with htmlFor.
formatSize(bytes: number) => string--Formats file sizes and maxSize. Default decimal units, such as 482 KB.
labelsFileUploadLabels--The words: prompt ({browse} marks the button), browse, list, remove, retry, uploading ({name}), done, tooLarge ({name}, {size}), wrongType ({name}, {accept}), tooMany ({max}), rejected ({name}, {error}). Defaults: Drop files here or {browse}, Browse files, Files, Remove {name}, Retry {name}, Uploading {name}, Uploaded, Larger than {size}, Not an accepted file type, You can add up to {max} files, {name}, {error}.

Anatomy

PartDescription
rootThe column: dropzone, error, then the file list.
dropzoneThe dashed area that takes drops and opens the picker, in the sunken fill, with the control border. Hover adds the hover overlay.
inputThe real input type=file, visually hidden inside the dropzone.
iconThe 16px upload icon in a raised circle. The circle is a fixed 32, 40 or 48px, as an illustration outside the control sizes.
promptThe prompt words and the browse button.
browseBrowse files, drawn as a secondary Button. Part of the label, not a second control.
hintThe types and the limits, in secondary text.
errorWhy the field is invalid, or the maxFiles notice, in the danger color.
listThe list of picked files.
itemA file tile laid out as AttachmentPreview: thumbnail, name over size, progress or error, then the mark and buttons.
thumbnailThe image or the file type icon.
imageThe thumbnail of an image file.
bodyThe name over the size, the progress bar or the error.
nameThe file name on one line, cut with an ellipsis.
metaThe file size.
itemErrorWhy the upload failed or why the file was turned down.
doneThe success check mark of an uploaded file.
retryThe ghost icon Button that retries a failed upload. Not shown on turned-down files.
removeThe ghost icon Button that removes the file.

Accessibility

Role: generic

KeysAction
TabMoves to the file input in the dropzone, then to each file's retry and remove buttons.
Enter / SpaceOn the dropzone, opens the file picker. On a retry or remove button, retries or removes that file.
  • The dropzone holds a label around a real input type=file, so the picker, accept and multiple are the browser's own, and screen readers name the input by the prompt and the browse words. The hint sits outside the label, so it is read once, as the description. Enter and Space both open the picker.
  • The input is described by the hint, the error and the file list.
  • invalid sets aria-invalid on the input; error is shown under the dropzone in words, not by the red edge alone. A maxFiles notice is announced as an alert. Each file turned down by accept or maxSize is announced in a hidden status, EN `brief.pdf, Larger than 5 MB`.
  • Each file's remove button is named with it, EN `Remove brief.pdf`; the retry button EN `Retry brief.pdf`. Both are described by the file's error.
  • An uploading file has a progress bar named EN `Uploading brief.pdf`. An uploaded file has a check mark with the hidden word Uploaded.
  • After a file is removed, focus goes back to the input, so it is not lost.

Install

npx werewolf-ui add file-upload

Run npx werewolf-ui init once in your app before adding components. Init sets up tokens, their CSS import and agent rules. Add installs this component and its dependencies while preserving your theme. See Installation for requirements and the alternative registry setup.