Actions
Drop Zone
A surface that accepts files dropped from the desktop.
When to use it: Use it when dragging files is a convenient alternative to choosing them in a file picker.
Example
Drop PNG or JPEG images here
No images selected
import { useState } from "react";
import { DropZone, FileTrigger } from "@comp0/react";
import { ArrowUpTrayIcon } from "@heroicons/react/20/solid";
export function Example() {
const [files, setFiles] = useState<string[]>([]);
const updateFiles = (selected: File[]) => setFiles(selected.map((file) => file.name));
return (
<div className="flex w-full max-w-sm flex-col gap-2">
<DropZone
accept="image/png,image/jpeg"
onDrop={updateFiles}
className="flex min-h-40 flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed border-zinc-950/20 p-6 text-center transition-colors data-accept:border-teal-600 data-accept:bg-teal-50 data-reject:border-red-600 data-reject:bg-red-50 dark:border-white/20 dark:data-accept:border-teal-400 dark:data-accept:bg-teal-950/40 dark:data-reject:border-red-400 dark:data-reject:bg-red-950/40"
>
<ArrowUpTrayIcon className="size-6 text-zinc-500" aria-hidden="true" />
<p className="text-sm text-zinc-600 dark:text-zinc-300">Drop PNG or JPEG images here</p>
<FileTrigger
accept="image/png,image/jpeg"
multiple
className="cursor-pointer rounded bg-zinc-900 px-3 py-2 text-sm text-white has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-teal-600 dark:bg-zinc-100 dark:text-zinc-950 dark:has-focus-visible:outline-teal-400"
onChange={(event) => updateFiles([...(event.currentTarget.files ?? [])])}
>
Choose images
</FileTrigger>
</DropZone>
<p className="text-sm text-zinc-600 dark:text-zinc-400" aria-live="polite">
{files.length ? files.join(", ") : "No images selected"}
</p>
</div>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
DropZone
Native div that accepts matching files dropped from outside the page. Owns a DOM element.
FileTriggerOptional
Optional native file chooser using the same accept filter. Owns a DOM element.
Step by step
- 1
Add the main part
Start with a DropZone and explain which files it accepts in visible text.
- 2
Add the supporting parts
Add a FileTrigger inside it so keyboard and touch users can choose the same files.
- 3
Make the behavior clear
Use accept on both parts and handle accepted and rejected drops separately when the distinction matters.
Exampletsx <DropZone accept="image/*" onDrop={upload}> <FileTrigger accept="image/*">Choose images</FileTrigger> </DropZone>;
Keyboard
Forms and accessibility
DropZone does not create form values. FileTrigger submits selected files with its native name prop.
Accessibility checklist
- Describe accepted files in visible text; accept filters files but does not name the task for assistive technology.
- Always include FileTrigger or another keyboard-reachable file chooser; dropping is pointer-only.
- Show rejected files and upload errors in text, not only through the drop zone color.
API reference
import { DropZone, FileTrigger } from "@comp0/react";DropZone
DOM elementNative div that accepts matching files dropped from outside the page.
| Prop | Type | Description |
|---|---|---|
accept | string | Comma-separated MIME types, wildcards, or file extensions. |
onDrop | (files: File[]) => void | Receives files only when every file matches accept. |
onDropRejected | (files: File[]) => void | Receives files when one or more files do not match accept. |
disabled | boolean | Stops the zone from accepting file drags or drops. |
FileTrigger
OptionalDOM elementOptional native file chooser using the same accept filter.
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
DropZone
| Style hook | Meaning |
|---|---|
[data-active] | A file drag is over the zone. |
[data-accept] | The dragged files match accept. |
[data-reject] | One or more dragged files do not match accept. |
[data-disabled] | The zone ignores file drops. |