File dropzone
A generous target for dropping one or more files.
$ pnpm dlx shadcn@4.23.0 add @xzero-ui/file-dropzone- Type: Component (inputs)
- Access: Free, open source
- Page: https://ui.xzero.co.uk/components/file-dropzone
- Markdown: https://ui.xzero.co.uk/components/file-dropzone/markdown
- Registry item: https://mcp.xzero.co.uk/r/file-dropzone.json
- Source file:
registry/components/file-dropzone/file-dropzone.tsx - Dependencies: motion, lucide-react
- Keywords: field, files, react file upload, file dropzone, drag and drop upload, upload with progress, file uploader, dropzone component, multi file upload
When to use
- Attachments and uploads with drag and drop plus a file picker.
- Uploads that need per-file progress, retry, and cancel through onUpload.
When not to use
- Use input with type file only when a native picker with no list is enough.
- Use empty-state when the drop area is the only thing on an empty page and needs a larger call to action.
Installation
Register both Xzero UI namespaces in your project's components.json:
{
"registries": {
"@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
"@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
}
}
npx shadcn@4.23.0 add @xzero-ui/file-dropzone
Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.
Usage
import { FileDropzone } from "@/components/xzero/file-dropzone/file-dropzone";
export function AttachmentUpload() {
return (
<FileDropzone
accept=".pdf,image/*"
maxFiles={3}
maxSize={10 * 1024 * 1024}
onUpload={async (item, { onProgress, signal }) => {
await uploadFile(item.file!, { onProgress, signal });
}}
/>
);
}
Examples
Folding prompt with a size limit
<FileDropzone
label="Add receipts"
accept="image/*,.pdf"
maxFiles={10}
maxSize={5 * 1024 * 1024}
compactAt={2}
listPlacement="inside"
onFilesChange={setFiles}
/>
API reference
FileDropzone
A drop target and file picker with a file list that can run uploads with progress, retry, and removal.
| Prop | Type | Default | Description |
|---|---|---|---|
accept | string | – | Accepted types, as in the native accept attribute (".pdf", "image/*"). Others are rejected with an error. |
multiple | boolean | true | Allow several files. |
maxFiles | number | 5 | Most files the list holds. |
onFilesChange | (files: File[]) => void | – | Called with the selected File objects whenever the list changes. |
label | string | "Add files" | Main prompt on the drop target. |
description | string | "Drop files here or choose from your device" | Secondary prompt. |
defaultItems | FileDropzoneItem[] | – | Rows present at mount, such as earlier uploads. They render without an entrance. |
onUpload | (item: FileDropzoneItem, options: { onProgress: (percent: number) => void; signal: AbortSignal }) => Promise<void> | – | Uploads each added file. Report progress, resolve on success, reject with an Error whose message becomes the row's reason. Removing a row aborts the signal. |
maxSize | number | – | Bytes. Larger files fail with a size reason and never upload. |
listPlacement | "below" | "inside" | "below" | Render the list under the target or inside its edge. |
note | string | – | Replaces the small line under the description (defaults to accepted types or the file limit). |
dropLabel | string | – | Label while files hover over the target. |
compactAt | number | – | Once the list holds this many files, the prompt folds to a slim bar. |
FileDropzoneItem
One row in the list, used by defaultItems and onUpload.
| Prop | Type | Default | Description |
|---|---|---|---|
id (required) | string | – | Stable id. |
name (required) | string | – | File name; its extension picks the type icon. |
size (required) | number | – | Bytes. |
status | FileDropzoneStatus | – | Upload state of the row. |
progress | number | – | Upload percent. |
error | string | – | Failure reason shown on the row. |
retryable | boolean | – | Shows a retry button when failed. |
file | File | – | The File, for items the visitor added. |
preview | string | – | Thumbnail URL shown instead of the type icon. Image files added by the visitor get an object URL automatically, revoked when the row is removed or the component unmounts. Falls back to the icon if the image fails. |
Keyboard interactions
| Keys | Action |
|---|---|
| Enter / Space | On the drop target, opens the native file picker. |
| ArrowUp / ArrowDown | In the file list, moves focus to the same action (remove or retry) on the adjacent row. |
| Delete / Backspace | In the file list, removes the focused row. |
| ⌘ + V / Ctrl + V | Pastes files from the clipboard, such as a screenshot, while the pointer is over the dropzone or focus is inside it. Ignored inside text fields. |
Accessibility
- The target is a native button; the hidden file input is removed from the tab order.
- Uploading rows expose role="progressbar" with aria-valuenow; remove and retry buttons are labelled with the file name.
- Additions, completions, and failures are announced through a polite role="status" region; rejection errors use role="alert".
- Removing a row moves focus to a neighbouring row so keyboard users are not dropped.
Motion
- The icon lifts while files hover, the edge animates, rows enter and collapse on a smooth spring, and progress bars ease toward their target.
- Reduced motion replaces height and lift animations with short fades and keeps progress changes instant.
Responsive behavior
- The file list is a container query: below 440px retry becomes icon-only, and below 300px the file icon hides.
- File names and failure reasons ellipsize instead of wrapping.
- The whole target is one tap area that opens the native picker, so it works without drag and drop on touch.
Performance
- The dashed edge is refitted by a ResizeObserver, plus a rAF loop only while the corner radius is transitioning.
- Rows are not virtualized and maxFiles defaults to 5; row entrances stagger up to eight steps.
Notes for AI
- Use for file attachments and uploads. Without onUpload the list shows plain selections; read them from onFilesChange and submit yourself.
- Files can be dropped, picked, or pasted. Pass preview on defaultItems (for example a stored thumbnail URL) to show earlier image uploads as thumbnails.
- With onUpload, the component manages per-file status, retry, and abort; wire it to fetch or XHR and pass signal through.
- Also exports formatFileSize(bytes) and the FileDropzoneItem, FileDropzoneStatus, and FileDropzoneUpload types.
Related
- Progress: Show how much of a known task is complete.
- Input: A single line field with clear labels and useful states.
- Empty state: A useful next step when there is nothing to show yet.
Also in editors
- Rich text editor: A lightweight editor with markdown shortcuts, a floating toolbar, a slash menu, and HTML and markdown output.
- Signature pad: Smooth ink that thins with speed, with undo, replay, and PNG or SVG export.
- Avatar upload: Click or drop a photo onto the avatar to crop it in place with drag, pinch and zoom. It uploads through a progress ring and settles, with clear errors and retry.
Guidance for AI tools
File dropzone: A generous target for dropping one or more files. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.
Full library index: https://ui.xzero.co.uk/llms.txt