Forms

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.

Preview
01 Installation

Install

Pull Dropzone from the barrel for everyday use, or the granular path when you want a tighter bundle.

tsxBarrel1 line
import { Dropzone } from "@brika/clay";
tsxGranular1 line
import { Dropzone } from "@brika/clay/components/dropzone";
02 Usage

A minimal example

Drop this into a page. Native HTML attributes pass through to the underlying primitive.

tsxDropzone.tsx52 lines
'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 &amp; 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>
  );
}
03 Examples

Dropzone, every way

Images Only

Restrict to images and accept multiple files with `accept` and `multiple`.

Maximum File Size

Reject oversized files with `maxSize` and report them via `onReject`.

04 Accessibility

Accessibility

  • The surface is a role="button" with tabIndex={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-active attribute, decorative state that does not change the accessible name.
  • Pass an aria-label (or include a DropzoneTitle) so the purpose of the surface is announced.
05 Tokens 13 theme-overridable

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
ColorFill, border, and text colors.7
  • --dropzone-bordercolorvar(--border)
    components.dropzone.border

    Dashed border color at rest.

  • --dropzone-containercolortransparent
    components.dropzone.container

    Surface background at rest.

  • --dropzone-active-bordercolorvar(--primary)
    components.dropzone.activeBorder

    Dashed border color while a file is dragged over the surface.

  • --dropzone-active-containercolorcolor-mix(in oklch, var(--primary) 8%, transparent)
    components.dropzone.activeContainer

    Surface background while a file is dragged over (and on hover).

  • --dropzone-iconcolorvar(--muted-foreground)
    components.dropzone.icon

    Color of the upload glyph.

  • --dropzone-titlecolorvar(--foreground)
    components.dropzone.title

    Primary instruction text color.

  • --dropzone-descriptioncolorvar(--muted-foreground)
    components.dropzone.description

    Secondary hint text color (accepted types, size limits).

GeometrySizes, lengths, and corner radii.4
  • --dropzone-radiusradiusvar(--radius-container)
    components.dropzone.radius

    Corner radius of the dropzone surface.

  • --dropzone-padding-xsizecalc(var(--spacing) * 6)
    components.dropzone.paddingX

    Inline padding inside the dropzone.

  • --dropzone-padding-ysizecalc(var(--spacing) * 6)
    components.dropzone.paddingY

    Block padding inside the dropzone.

  • --dropzone-gapsizecalc(var(--spacing) * 2)
    components.dropzone.gap

    Gap between adjacent children inside the dropzone.

MotionAnimation duration and easing.2
  • --dropzone-durationdurationvar(--motion-standard-duration)
    components.dropzone.duration

    Transition duration for dropzone state changes.

  • --dropzone-easingeasingvar(--motion-standard-easing)
    components.dropzone.easing

    Transition 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.

jsonmy-theme.json12 lines
{
  "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.

06 API reference

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
  • string
    Comma-separated list of accepted types, forwarded to the native input.
  • disableddefault false
    boolean
    Disable dragging, clicking, and keyboard activation.
  • number
    Reject files larger than this many bytes.
  • multipledefault false
    boolean
    Allow dropping or selecting more than one file.
  • (files: File[]) => void
    Fires with the accepted files from a drop or the picker.
  • (rejections: DropzoneRejection[]) => void
    Fires with any files filtered out by maxSize.

<DropzoneContext />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<DropzoneDescription />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<DropzoneIcon />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<DropzoneTitle />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.