v1.0

Dropzone

Drag-and-drop file drop area with a built-in trigger, accept and size validation, and an onFilesAccepted callback.

Drag and drop file(s)

Max 10 MB eachOnly PDF, ZIP or Images

Description

Dropzone is a drag-and-drop (or click) file drop area with a built-in trigger and an auto-derived description. It validates files against accept and maxSize, emits the accepted File objects through onFilesAccepted, and surfaces rejections inline.

On its own it is a lightweight file picker: give it an onFilesAccepted and do whatever you want with the files. Dropped inside Create UI Pro's FileUpload, it inherits accept, maxSize, multiple, and disabled from the parent and feeds accepted files straight into the upload list — no wiring needed.

Reach for it for attachment inputs, image and avatar pickers, and import flows. For a bare hidden file input with no drop area, a plain Field with a native input is enough.

Installation

pnpm dlx @create-ui/cli add dropzone

Anatomy

Render Dropzone on its own and handle the accepted files:

<Dropzone
  accept="image/*"
  maxSize={10 * 1024 * 1024}
  onFilesAccepted={(files) => {
    // upload or store the accepted File[]
  }}
/>

Compose your own content by passing children — the drop behaviour and validation stay intact:

<Dropzone accept="image/*">
  <DropzoneHeader>
    <DropzoneIcon />
    <DropzoneHeading>
      <DropzoneTitle>Drop your images here</DropzoneTitle>
      <DropzoneDescription>PNG, JPG or GIF</DropzoneDescription>
    </DropzoneHeading>
  </DropzoneHeader>
  <DropzoneSeparator>or</DropzoneSeparator>
  <DropzoneTrigger>Browse</DropzoneTrigger>
</Dropzone>

Usage

import { Dropzone } from "@/components/ui/dropzone"
<Dropzone
  multiple
  accept=".pdf,.zip,image/*"
  maxSize={25 * 1024 * 1024}
  onFilesAccepted={(files) => console.log(files)}
  onFileReject={(file, reason) => console.warn(file.name, reason)}
/>

onFilesAccepted receives the files that passed validation; onFileReject fires per rejected file with the reason. When accept or maxSize reject a file, the dropzone also shows the message inline and derives its default description from accept and maxSize.

Examples

Sizes

Three layouts via size: sm (a dense single row), md (a compact inline bar), and lg (the default tall stack).

Drag and drop file(s)

Drag and drop file(s) or

Max 25 MB each, Only ZIP or PDF.

Drag and drop file(s)

Max 25 MB eachOnly ZIP or PDF

Labels

Swap just the copy without composing children by passing title and description. Compose children (including DropzoneTrigger for the button text) when you need full control over the layout.

Drag files here or click to browse

PDF, DOCX, PNG, or JPG up to 10 MB.

Custom content

Pass children to supply your own icon, title, description, separator, and trigger.

Drop your images here

PNG, JPG or GIF, up to 10 MB

Error

Force the error appearance with error and errorMessage; it is also derived automatically from rejected files.

Drag and drop file(s)

Max 25 MB eachFile exceeds the 25MB limit

Disabled

Set disabled to dim the area and turn off drag and click.

Drag and drop file(s)

Max 25 MB eachOnly ZIP or PDF

Accessibility

The drop area opens the file picker on click for pointer users; keyboard users reach the picker through the built-in trigger button. The hidden <input type="file"> carries accept and multiple.

KeyDescription
TabMoves focus to the trigger button.
Space EnterActivates the trigger (open picker).

ARIA notes:

  • The area sets aria-disabled when disabled and aria-invalid in the error state, with data-state reflecting idle, dragging, error, or disabled.
  • Clicks on nested buttons, links, labels, and inputs do not re-open the picker.

Styling

Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):

<Dropzone className="min-h-40" />

Data slots and attributes: the component sets these for CSS targeting.

  • data-slot="dropzone" with data-state="idle" | "dragging" | "error" | "disabled" and data-size on the drop area.
  • data-slot="dropzone-trigger" on the picker button.
  • data-slot="dropzone-icon", -title, -description, -header, -heading, -separator on the content parts.

Target the dragging state in CSS:

[data-slot="dropzone"][data-state="dragging"] {
  /* ... */
}
  • File Upload: Create UI Pro's per-file upload list that composes Dropzone for its drop area.
  • Button: the trigger button.
  • Field: the form-field wrapper for plain inputs.

API Reference

Dropzone

The drop area with a built-in trigger and auto-derived description. Renders a <div data-slot="dropzone"> and extends React.ComponentProps<"div">.

When rendered inside a provider (such as FileUpload), each of accept, maxSize, multiple, disabled, onFilesAccepted, and onFileReject falls back to the context value when the prop is omitted. size never comes from context — it is always the prop (default lg).

Props

PropTypeDefaultDescription
acceptstring-Comma-separated extensions or MIME types to allow; mismatches reject.
maxSizenumber-Max bytes per file; larger files are rejected.
multiplebooleanfalseAllow selecting more than one file.
disabledbooleanfalseDim the area and turn off drag and click.
errorboolean-Forces the error appearance (also derived from rejected files).
errorMessageReact.ReactNode-Message shown in the error state.
titleReact.ReactNode-Override the default headline.
descriptionReact.ReactNode-Override the auto description; pass null to suppress it.
onFilesAccepted(files: File[]) => void-Fires with the files that pass validation.
onFileReject(file: File, reason: string) => void-Fires per rejected file, with the reason.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-Custom content; replaces the default icon/title/trigger stack.

Variants

VariantOptionsDefaultDescription
size"sm" "md" "lg""lg"Layout: sm dense row, md inline bar, lg tall stack.

DropzoneTrigger

A button that opens the file picker, usable on its own or inside a Dropzone. Nested in a Dropzone, it reuses that area's hidden input; standalone (for example inside FileUpload with no drop area), it owns its own input and reads config from context. Extends the Button component's props (React.ComponentProps<typeof Button>) plus optional accept, maxSize, multiple, onFilesAccepted, and onFileReject for standalone use. Defaults to variant="neutral-solid" and size="lg".

useDropzone

The headless hook behind Dropzone, for fully custom drop areas. It owns the hidden input, drag tracking, and accept/size validation.

import { useDropzone } from "@/hooks/use-dropzone"

Options

OptionTypeDefaultDescription
acceptstring-Allowed extensions or MIME types.
maxSizenumber-Max bytes per file.
multiplebooleanfalseAllow more than one file.
disabledbooleanfalseDisable input and drop handling.
onFilesAccepted(files: File[]) => void-Fires with the validated files.
onFileReject(file: File, reason: string) => void-Fires per rejected file.

Returns

FieldTypeDescription
isDraggingbooleanWhether a drag is over the area.
rejectionMessagestring | nullThe latest rejection reason, if any.
clearRejection() => voidClear the rejection message.
openFileDialog() => voidOpen the native file picker.
getInputProps getDropzoneProps() => objectProp getters for the hidden input and the drop target.

Composition parts

Lower-level slots for custom drop areas. Each renders a styled <div> (or <p>) with a data-slot and merges className via cn().

PartRendersPurpose
DropzoneHeaderdivIcon + heading group.
DropzoneHeadingdivTitle + description group.
DropzoneIcondivThe glyph (cloud by default; pass children to swap).
DropzoneTitlepHeadline.
DropzoneDescriptiondivSupporting text.
DropzoneSeparatordivLabelled or divider.