Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Dropzone

A drag-and-drop (or click) upload area wired to useFiles - uploads through 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. With no prefix (and outside folder uploads), the server mints each key and the bytes go straight to storage on a presigned URL, or through your gateway when the adapter can’t sign. With a prefix or a folder path, each file gets an explicit key and streams through the 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.json

Usage

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 (files that don’t match accept, oversize files, files over the maxFiles limit, and failed uploads). Pass your own children to any of them to override the default look (the zone is a <button>, so keep them to phrasing content such as spans, icons and text), or wire onUploaded / onError to render state outside the dropzone entirely.

accept filters the file picker and is enforced on dropped files too, using the same rules: .ext suffixes, type/* wildcards, and exact MIME types.

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>

PropType
filesUseFilesResult

A `useFiles()` instance — the dropzone uploads through it.

TypeUseFilesResult
prefix?string

Key prefix (folder) for explicit keys, e.g. `"docs/"`. Empty = server mints the key.

Typestring
accept?string

Accepted file types, e.g. `"image/*"` or `".pdf,.docx"` — the file input's `accept` filter, also enforced on dropped files (non-matching files are reported as failed).

Typestring
directory?boolean

Accept whole folders: the picker selects a directory and dropped folders are traversed recursively, with relative paths preserved in keys (`prefix + folder/sub/file.ext`).

Typeboolean
maxFiles?number

Max files per drop. Default 1, or unlimited when `directory` is set.

Typenumber
maxSize?number

Max bytes per file; larger files are reported as failed.

Typenumber
onUploaded?(entry: UploadedEntry) => void

Called after each successful upload.

Type(entry: UploadedEntry) => void
onError?(error: Error, file: File) => void

Called for each file that fails to upload or is rejected client-side.

Type(error: Error, file: File) => void
className?string
Typestring
children?ReactNode
TypeReactNode

<DropzoneContent>, <DropzoneEmptyState> and <DropzoneError> each accept className and children to override the default look.

Was this page helpful?