Dropzone
A drag-and-drop (or click) upload area wired to useFiles - streams bytes straight to your gateway, with composable success, prompt and error states.
A compound dropzone that uploads through a useFiles instance. Drag files in or click to open the picker; bytes stream straight to your gateway.
"use client";
import { demoFiles } from "@/lib/demo-files";
import {
Dropzone,
DropzoneContent,
DropzoneEmptyState,
DropzoneError,
} from "@/registry/files-sdk/dropzone/dropzone";
const Example = () => {
const files = demoFiles;
return (
<Dropzone accept="image/*" files={files} maxFiles={3} prefix="demo/">
<DropzoneContent />
<DropzoneEmptyState />
<DropzoneError />
</Dropzone>
);
};
export default Example;
Installation
npx shadcn@latest add https://files-sdk.dev/r/dropzone.jsonUsage
import { useFiles } from "files-sdk/react";
import {
Dropzone,
DropzoneContent,
DropzoneEmptyState,
DropzoneError,
} from "@/components/files-sdk/dropzone";
export function Uploader() {
const files = useFiles({ endpoint: "/api/files" });
return (
<Dropzone accept="image/*" files={files} prefix="docs/" maxFiles={5}>
<DropzoneContent />
<DropzoneEmptyState />
<DropzoneError />
</Dropzone>
);
}
DropzoneEmptyState renders the prompt — and keeps rendering it after uploads, so users can keep adding files. DropzoneContent shows a success summary (“3 files uploaded”) once something lands, and DropzoneError surfaces failures (rejected files, oversize files, files over the maxFiles limit). Pass your own children to any of them to override the default look, or wire onUploaded / onError to render state outside the dropzone entirely.
Folder uploads
Set directory to accept whole folders. The click-to-browse picker becomes a directory picker, dropped folders are traversed recursively, and each file keeps its relative path in the key: dropping my-skill/ with prefix="skills/" uploads skills/my-skill/references/api-guide.md, ready for a tree view in FileBrowser.
"use client";
import { demoFiles } from "@/lib/demo-files";
import {
Dropzone,
DropzoneContent,
DropzoneEmptyState,
DropzoneError,
} from "@/registry/files-sdk/dropzone/dropzone";
const Example = () => {
const files = demoFiles;
return (
<Dropzone directory files={files} prefix="skills/">
<DropzoneContent />
<DropzoneEmptyState />
<DropzoneError />
</Dropzone>
);
};
export default Example;
To let users pick the destination themselves, drive prefix from an input:
import { useState } from "react";
import { useFiles } from "files-sdk/react";
import { Input } from "@/components/ui/input";
import {
Dropzone,
DropzoneContent,
DropzoneEmptyState,
DropzoneError,
} from "@/components/files-sdk/dropzone";
export function FolderUploader() {
const files = useFiles({ endpoint: "/api/files" });
const [destination, setDestination] = useState("references/");
return (
<div className="flex flex-col gap-2">
<Input
aria-label="Destination folder"
onChange={(event) => setDestination(event.target.value)}
value={destination}
/>
<Dropzone directory files={files} prefix={destination}>
<DropzoneContent />
<DropzoneEmptyState />
<DropzoneError />
</Dropzone>
</div>
);
}
With directory set, maxFiles defaults to unlimited; pass a number to cap the total file count per drop.
Props
<Dropzone>
DropzoneProps: Cannot read properties of undefined (reading 'ESNext')<DropzoneContent>, <DropzoneEmptyState> and <DropzoneError> each accept className and children to override the default look.