Dropzone
A drag-and-drop file upload zone with accessible keyboard navigation and visual feedback.
Overview
The Dropzone component provides an accessible drag-and-drop area for file uploads. Built on React Aria, it includes keyboard navigation, screen reader support, file picker integration, type/size/count validation, and visual feedback for hover, drag target, disabled, and rejected states.
Usage
Dropzone provides the upload interaction. Compose visual content inside it with Empty State sub-components.
import { Button } from "@tilt-legal/cubitt-components/button";
import {
Dropzone,
DropzoneAction,
DropzoneFolderAnimation,
} from "@tilt-legal/cubitt-components/dropzone";
import {
EmptyStateDescription,
EmptyStateHeading,
EmptyStateMedia,
EmptyStateTitle,
} from "@tilt-legal/cubitt-components/empty-state";
import { CloudUpload } from "@tilt-legal/cubitt-icons/ui/outline";<Dropzone onFileDrop={(files) => console.log(files)}>
<EmptyStateMedia>
<CloudUpload />
</EmptyStateMedia>
<EmptyStateHeading>
<EmptyStateTitle>Drop files here</EmptyStateTitle>
<EmptyStateDescription>or click to browse</EmptyStateDescription>
</EmptyStateHeading>
</Dropzone>Examples
Variants
Switch between the default, ghost, and minimal dropzone variants.
const variants = ["default", "ghost", "minimal"] as const;
const [variant, setVariant] = useState<"default" | "ghost" | "minimal">(
"default"
);
<>
<Dropzone variant={variant}>
<EmptyStateMedia>
<CloudUpload />
</EmptyStateMedia>
<EmptyStateHeading>
<EmptyStateTitle>Drop files here</EmptyStateTitle>
<EmptyStateDescription>
Switch variants to compare surface and drag states.
</EmptyStateDescription>
</EmptyStateHeading>
</Dropzone>
<PreviewFooter>
<ToggleGroup
aria-label="Dropzone variant"
groupVariant="segmented"
multiple={false}
onValueChange={(value) => {
if (variants.includes(value as (typeof variants)[number])) {
setVariant(value as (typeof variants)[number]);
}
}}
value={variant}
>
<ToggleGroupItem value="default">Default</ToggleGroupItem>
<ToggleGroupItem value="ghost">Ghost</ToggleGroupItem>
<ToggleGroupItem value="minimal">Minimal</ToggleGroupItem>
</ToggleGroup>
</PreviewFooter>
</>;Folder Animation
Use the animated folder for drag-over feedback. File type icons are based on the parent accept prop.
<Dropzone
accept={[".pdf", ".docx", ".xlsx"]}
onFileDrop={(files) => console.log(files)}
>
<DropzoneFolderAnimation />
<EmptyStateHeading>
<EmptyStateTitle>Upload documents</EmptyStateTitle>
<EmptyStateDescription>
PDF, Word, and Excel files accepted
</EmptyStateDescription>
</EmptyStateHeading>
</Dropzone>;Action Button
Add an explicit action inside the dropzone while preserving the full drag-and-drop target.
<Dropzone onFileDrop={(files) => console.log(files)}>
<EmptyStateMedia>
<CloudUpload />
</EmptyStateMedia>
<EmptyStateHeading>
<EmptyStateTitle>Drag and drop files</EmptyStateTitle>
<EmptyStateDescription>PDF, DOCX, or images up to 10MB</EmptyStateDescription>
</EmptyStateHeading>
<DropzoneAction>
<Button>Browse files</Button>
</DropzoneAction>
</Dropzone>;API Reference
Dropzone
Root component that handles drag-and-drop and file selection.
| Prop | Type | Default | Description |
|---|---|---|---|
onFileDrop | (files: File[]) => void | - | Callback when valid files are dropped or selected |
onFilesRejected | (rejections: FileRejection[]) => void | - | Callback when files are rejected by type, size, or count validation |
accept | string | readonly string[] | - | MIME types or file extensions for the picker and runtime validation |
multiple | boolean | true | Allow multiple file selection |
maxSize | number | - | Maximum file size in bytes |
maxFiles | number | - | Maximum number of accepted files |
isDisabled | boolean | false | Disable the dropzone |
disableClick | boolean | false | Disable click-to-browse while preserving drag-and-drop |
variant | "default" | "minimal" | "ghost" | "default" | Visual variant |
getDropOperation | (types) => DropOperation | - | Custom React Aria drop operation resolver |
onDrop | (e: DropEvent) => void | - | Raw React Aria drop event callback, called after Cubitt file parsing |
children | ReactNode | - | Composed visual content |
className | string | - | Additional CSS classes |
Dropzone also accepts the remaining React Aria DropZone props, except children is owned by Cubitt's composed content API.
FileRejection
type FileRejection = {
file: File;
reason: "type" | "size" | "count";
message: string;
};DropzoneFolderAnimation
Animated folder component with documents that fan out when opened. It responds to drag events from the parent Dropzone.
| Prop | Type | Default | Description |
|---|---|---|---|
showFileTypeIcons | boolean | true | Show file type icons on documents based on parent accept prop |
hideFrontPanel | boolean | false | Hide the front folder panel for visual debugging |
className | string | - | Additional CSS classes |
DropzoneAction
Wrapper for action buttons. Handles click propagation so the nested button opens the file picker without double-triggering the root.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Button or interactive element |
className | string | - | Additional CSS classes |