Drop zone that grows as files land, with per-file progress, errors and retry.
Set your access key from API keys as NEXTUI_TOKEN in .env.local and add the @nextui namespace below to components.json once. Then every item installs by name. Each install counts as a copy on a free account.
$pnpm dlx shadcn@latest add @nextui/dropzone
1{2"registries": {3"@nextui": {4"url": "https://nextui.dev/r/{name}.json",5"headers": { "Authorization": "Bearer ${NEXTUI_TOKEN}" }6}7}8}
1"use client";23import { useState } from "react";4import { FileDropzone, type DropzoneFile } from "@/components/nextui/dropzone";56export function Upload() {7const [files, setFiles] = useState<DropzoneFile[]>([]);8const update = (id: string, patch: Partial<DropzoneFile>) =>9setFiles((list) => list.map((f) => (f.id === id ? { ...f, ...patch } : f)));1011return (12<FileDropzone13files={files}14maxSizeMB={25}15accept="image/*,.pdf"16onFiles={(accepted, rejected) => {17const added = accepted.map((file) => ({ id: crypto.randomUUID(), name: file.name, size: file.size, status: "uploading" as const, progress: 0 }));18const tooBig = rejected.map((file) => ({ id: crypto.randomUUID(), name: file.name, size: file.size, status: "rejected" as const }));19setFiles((list) => [...list, ...added, ...tooBig]);20// Start your uploads here and call update(id, { progress }) / update(id, { status: "done" }).21}}22onRemove={(id) => setFiles((list) => list.filter((f) => f.id !== id))}23onRetry={(id) => update(id, { status: "uploading", progress: 0 })}24onClear={() => setFiles([])}25/>26);27}
| Prop | Type | Default | Description |
|---|---|---|---|
| files | DropzoneFile[] | [] | The list to show: id, name, size in bytes, status, progress (0 to 1) and an optional error |
| onFiles | (accepted: File[], rejected: File[]) => void | – | Files dropped or picked; rejected ones are larger than maxSizeMB |
| onRemove | (id: string) => void | – | The × on a row was clicked |
| onRetry | (id: string) => void | – | The retry icon on a failed row was clicked |
| onClear | () => void | – | Clear was clicked once every upload settled |
| accept | string | – | Allowed types for the file picker, e.g. "image/*,.pdf" |
| maxSizeMB | number | 25 | Largest file allowed, in MB |
| multiple | boolean | true | Allow picking several files at once |
| hint | string | "Up to {maxSizeMB} MB each" | Line under the drop zone |
| demo | boolean | false | Play the scripted upload from the preview instead of using files |
| className | string | – | Extra class on the root |
Sign in to see the code
Preview it freely. A free account copies code and prompts a few times a day.