Dropzone
A drag-and-drop file surface that also opens the native picker on click, with a dashed border that highlights while a file is dragged over it.
Install
Pull Dropzone from the barrel for everyday use, or the granular path when you want a tighter bundle.
import { Dropzone } from "@brika/clay";import { Dropzone } from "@brika/clay/components/dropzone";A minimal example
Drop this into a page. Native HTML attributes pass through to the underlying primitive.
'use client';
import {
Dropzone,
DropzoneDescription,
DropzoneIcon,
DropzoneTitle,
} from '@brika/clay/components/dropzone';
import {
FileUploadItem,
FileUploadItemContent,
FileUploadItemName,
FileUploadItemPreview,
FileUploadItemRemove,
FileUploadItemSize,
FileUploadList,
} from '@brika/clay/components/file-upload';
import { FileText, ImageIcon } from 'lucide-react';
import { useState } from 'react';
/** Drag a file in or click to browse; the selection renders below. */
export default function DropzoneDefaultDemo() {
const [file, setFile] = useState<File | null>(null);
return (
<div className="w-full max-w-md space-y-3">
<Dropzone
aria-label="Upload a file"
className="min-h-44"
onDrop={(files) => setFile(files[0] ?? null)}
>
<DropzoneIcon />
<DropzoneTitle>Drag & drop a file here</DropzoneTitle>
<DropzoneDescription>or click to browse from your device</DropzoneDescription>
</Dropzone>
{file && (
<FileUploadList>
<FileUploadItem>
<FileUploadItemPreview>
{file.type.startsWith('image/') ? <ImageIcon aria-hidden /> : <FileText aria-hidden />}
</FileUploadItemPreview>
<FileUploadItemContent>
<FileUploadItemName>{file.name}</FileUploadItemName>
<FileUploadItemSize bytes={file.size} />
</FileUploadItemContent>
<FileUploadItemRemove onClick={() => setFile(null)} />
</FileUploadItem>
</FileUploadList>
)}
</div>
);
}Dropzone, every way
Accessibility
- The surface is a
role="button"withtabIndex={0}; Enter or Space opens the OS file picker. - A visually hidden native
<input type="file">performs the actual selection so keyboard and assistive-tech users are never blocked. - The drag-over highlight is driven by a
data-drag-activeattribute, decorative state that does not change the accessible name. - Pass an
aria-label(or include aDropzoneTitle) so the purpose of the surface is announced.
Theme tokens
Every CSS variable Dropzone reads, with its default and the
dotted path you'd write in a ThemeConfig JSON
to override it. Set any of these in your theme to retune Dropzone
without touching component code.
Dropzone tokens
13 tokens--dropzone-bordercolorvar(--border)components.dropzone.borderDashed border color at rest.
--dropzone-containercolortransparentcomponents.dropzone.containerSurface background at rest.
--dropzone-active-bordercolorvar(--primary)components.dropzone.activeBorderDashed border color while a file is dragged over the surface.
--dropzone-active-containercolorcolor-mix(in oklch, var(--primary) 8%, transparent)components.dropzone.activeContainerSurface background while a file is dragged over (and on hover).
--dropzone-iconcolorvar(--muted-foreground)components.dropzone.iconColor of the upload glyph.
--dropzone-titlecolorvar(--foreground)components.dropzone.titlePrimary instruction text color.
--dropzone-descriptioncolorvar(--muted-foreground)components.dropzone.descriptionSecondary hint text color (accepted types, size limits).
--dropzone-radiusradiusvar(--radius-container)components.dropzone.radiusCorner radius of the dropzone surface.
--dropzone-padding-xsizecalc(var(--spacing) * 6)components.dropzone.paddingXInline padding inside the dropzone.
--dropzone-padding-ysizecalc(var(--spacing) * 6)components.dropzone.paddingYBlock padding inside the dropzone.
--dropzone-gapsizecalc(var(--spacing) * 2)components.dropzone.gapGap between adjacent children inside the dropzone.
--dropzone-durationdurationvar(--motion-standard-duration)components.dropzone.durationTransition duration for dropzone state changes.
--dropzone-easingeasingvar(--motion-standard-easing)components.dropzone.easingTransition easing for dropzone state changes.
Override in a theme
Authoring a theme is plain JSON. Drop overrides under
components.dropzone, the names
are camelCase versions of the variable suffix.
{
"id": "my-theme",
"name": "My Theme",
"description": "...",
"accentSwatches": ["#000"],
"components": {
"dropzone": {
"paddingX": "calc(var(--spacing) * 6)"
}
}
}
Then apply with applyTheme(myTheme)
(see the theming guide),
scope it to a subtree with themeToCssVars(myTheme, mode),
or consume any token directly in your own JSX with the matching Tailwind utility
(e.g. bg-dropzone-…) — see the
chips on each token row.
API reference
Props specific to Dropzone. Native HTML attributes pass through to the underlying primitive, see the component source on GitHub for the full type signature.
<Dropzone />
6 props- stringComma-separated list of accepted types, forwarded to the native input.
- disableddefault
falsebooleanDisable dragging, clicking, and keyboard activation. - numberReject files larger than this many bytes.
- multipledefault
falsebooleanAllow dropping or selecting more than one file. - (files: File[]) => voidFires with the accepted files from a drop or the picker.
- (rejections: DropzoneRejection[]) => voidFires with any files filtered out by
maxSize.
<DropzoneContext />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<DropzoneDescription />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<DropzoneIcon />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<DropzoneTitle />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.