WebberUI

File Dropzone

A drag-and-drop upload area — the magnetic dashed border leans toward the cursor and marches like a marquee, and the file list carries ring progress indicators.

Loading preview…
npx shadcn@latest add https://webberui.com/r/file-dropzone.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

0.12
<FileDropzone />

Installation

npx shadcn@latest add https://webberui.com/r/file-dropzone.json

Or, once registries are configured in components.json, install it as @webberui/file-dropzone.

Usage

import { FileDropzone } from "@/components/ui/file-dropzone";

<FileDropzone
  accept="image/*,.pdf"
  maxFiles={4}
  maxSize={10 * 1024 * 1024}
  onFilesAdded={(files) => console.log(files)}
/>

Wiring up a real upload

Without an upload prop, the component shows the animation with simulated progress; to connect a backend, pass an async function and report progress:

<FileDropzone
  upload={async (file, onProgress) => {
    await uploadToServer(file, {
      onUploadProgress: (e) => onProgress(e.loaded / e.total),
    });
  }}
/>

resolve shows the success checkmark; reject shows the failure state.

Props

PropTypeDefaultDescription
onFilesAdded(files: File[]) => voidFires when files pass validation and are added to the list
upload(file: File, onProgress: (p: number) => void) => Promise<void>Custom upload logic; onProgress reports 0–1. When omitted, progress is simulated
onRemove(file: File) => voidFires when a file is removed from the list
onReject(file: File, reason: "type" | "size" | "count") => voidFires when a file fails validation
acceptstringAccepted file types (same as the native accept); validation applies to drops as well
multiplebooleantrueAllow multiple files; when false, a new file replaces the old one
maxFilesnumberMaximum number of files in the list
maxSizenumberMaximum size of a single file (bytes)
strengthnumber0.12Magnetic strength; border offset = the cursor's distance from centre × strength
labelstring"拖放檔案到這裡,或點擊選取"Main text of the drop area
disabledbooleanfalseDisables the whole drop area

How it works

  • Magnetic border: while a drag hovers, the dashed border offsets toward the cursor on a spring (capped at 10px) and scales up slightly, while the dashes march like a marquee
  • Dragging in and out of child elements fires dragenter / dragleave repeatedly, so a depth counter internally prevents the border state from flickering
  • The progress ring advances smoothly through SVG stroke-dashoffset driven by a spring, popping a checkmark on completion and showing a warning on failure
  • accept validation supports extensions (.pdf), wildcards (image/*), and full MIME types, and applies to both drops and picks
  • The input value is cleared after a selection, so the same file can be picked again

Accessibility

  • The drop area is role="button", takes Tab focus, and opens the file picker with Enter / Space
  • The progress ring carries role="progressbar" and aria-valuenow, and the success / failure icons have matching aria-labels
  • The number of files uploading is announced to screen readers through aria-live="polite"
  • When the user has "reduce motion" enabled at the system level, the magnetic offset, marquee, and enter/exit animations are disabled, and the progress ring jumps straight to the current value

On this page