Skip to content
WANCRAFT

F.54Forms & Inputs

File Dropzone

Drag-and-drop or browse for files, with per-file validation, thumbnails, upload progress and retries.

NewReactMotionSVGTailwindadvanced

Overview

File Dropzone wraps a real <input type="file"> in a label, so clicking, tapping or pressing Space/Enter opens the native picker and dropped files go through the same path. While files hover over the zone the dashed border marches and the zone reports whether they will be accepted. Each file is validated against accept, maxSize and maxFiles (plus duplicates) with its own message, and listed with an extension badge or image thumbnail, its size and status. Uploads run through an optional onUpload(file, reportProgress, signal) callback — progress bars, retry and cancel come for free — or you can drive progress yourself through a controlled files list. A polite live region summarises every change. Nothing is sent anywhere unless your onUpload does it.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/file-dropzone.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/file-dropzone.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/file-dropzone.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { FileDropzone, type UploadHandler } from "@/components/ui/file-dropzone";

export function FileDropzoneExample() {
  // Replace with your real upload (fetch/XHR to a signed URL, etc.).
  const upload: UploadHandler = async (file, reportProgress, signal) => {
    for (let i = 1; i <= 10; i++) {
      if (signal.aborted) return;
      await new Promise((r) => setTimeout(r, 120));
      reportProgress(i / 10);
    }
  };

  return (
    <div className="w-full max-w-md">
      <FileDropzone maxSize={5 * 1024 * 1024} onUpload={upload} />
    </div>
  );
}

03Props

PropTypeDefaultDescription
acceptstring""Same syntax as <input accept> (".pdf,image/*"); empty accepts anything.
maxSizenumber5242880Maximum bytes per file (5 MB); 0 disables the check.
maxFilesnumber5Maximum accepted files; extra files are listed as rejected.
onUpload(file, reportProgress, signal) => Promise<void>—Uploads one file. Call reportProgress(0–1); resolve when done, reject with an Error to show its message. signal aborts on remove/unmount.
files / onFilesChangeDropzoneFile[] / (files) => void—Controlled list ({ id, file, status, progress, error?, rejected?, preview? }). Set progress/status yourself if you don't use onUpload.
previewsbooleantrueObject-URL thumbnails for images (revoked on remove and unmount).
labelstring"Drop files here"Call to action inside the zone.
size"sm" | "md""md"Zone padding.
accentstring"#FF7448"Drag highlight, progress and focus colour.
disabledbooleanfalseDisables the picker and drop target.
ref{ open(); addFiles(files); clear() }—Imperative handle: open the picker, add files programmatically, clear the list.

04Features

  • 01Real file input inside a label: click, tap, Space/Enter and drop share one validated path
  • 02Marching-ants border and accept/reject feedback while files hover
  • 03Per-file validation: type, size, count and duplicates, each with its own message
  • 04Extension badges or image thumbnails, sizes, progress bars, retry and cancel
  • 05onUpload(file, reportProgress, signal) or a fully controlled files list
motion · springmotion · layoutmotion · continuousinput · draginput · clickinput · keyboardinput · focus

05Accessibility

  • The picker is a native <input type="file">, named by the zone's call to action and described by the accepted types and limits.
  • A visible focus ring appears on the zone when the input has keyboard focus.
  • Each upload has a role="progressbar" with a value and a name ("Uploading brief.pdf").
  • Remove buttons are named per file ("Remove …" / "Cancel upload of …"); focus moves to the next file after removal.
  • Additions, rejections with reasons, completions and failures are announced in a polite live region.
  • prefers-reduced-motion stops the marching border and list springs.

06Limitations

  • Drag feedback can only check MIME types; extension rules are validated on drop. Validation is client-side convenience; enforce limits on the server too.