27.7k

DropZone

A drag-and-drop file upload area with file type filtering, size limits, and a file list preview.

Import

import { DropZone } from '@darkcode-ui/react';

DropZone is a composable component. You own the file state and render the file list yourself, which keeps the upload flow fully under your control.

Usage

import {DropZone} from "@darkcode-ui/react";

export function Default() {
  return (
    <DropZone className="w-full max-w-md">

Anatomy

Import the DropZone component and access all parts using dot notation.

import { DropZone } from '@darkcode-ui/react';

<DropZone>
  <DropZone.Area>
    <DropZone.Icon />
    <DropZone.Label>Drag files here or click to browse</DropZone.Label>
    <DropZone.Description>PDF, DOCX, PNG, or JPG up to 10 MB.</DropZone.Description>
    <DropZone.Trigger>Select files</DropZone.Trigger>
  </DropZone.Area>
  <DropZone.Input />
  <DropZone.FileList>
    <DropZone.FileItem status="uploading">
      <DropZone.FileFormatIcon format="PDF" color="red" />
      <DropZone.FileInfo>
        <DropZone.FileName>Proposal.pdf</DropZone.FileName>
        <DropZone.FileMeta>2.4 MB</DropZone.FileMeta>
      </DropZone.FileInfo>
      <DropZone.FileProgress value={68}>
        <DropZone.FileProgressTrack>
          <DropZone.FileProgressFill />
        </DropZone.FileProgressTrack>
      </DropZone.FileProgress>
      <DropZone.FileRetryTrigger />
      <DropZone.FileRemoveTrigger />
    </DropZone.FileItem>
  </DropZone.FileList>
</DropZone>;

With File List

Render DropZone.FileList with one DropZone.FileItem per file. The list animates items in and out as your state changes.

import {DropZone} from "@darkcode-ui/react";

export function WithFileList() {
  return (
    <DropZone className="w-full max-w-md">

Compact File List

Pass compact to DropZone.FileList for a denser layout, useful when many files are listed.

import {DropZone} from "@darkcode-ui/react";

export function CompactFileList() {
  return (
    <DropZone className="w-full max-w-md">

Disabled

Set isDisabled on DropZone.Area to prevent dropping. The Trigger inside the area is disabled automatically.

import {DropZone} from "@darkcode-ui/react";

export function Disabled() {
  return (
    <DropZone className="w-full max-w-md">

Image Only

Use the accept prop on DropZone.Input to restrict the file picker to specific file types.

import {DropZone} from "@darkcode-ui/react";

export function ImageOnly() {
  return (
    <DropZone className="w-full max-w-md">

Max Size Limit

Validate file size in your onSelect handler and mark oversized files with status="failed", then offer a retry.

"use client";

import {DropZone} from "@darkcode-ui/react";
import React from "react";

Multiple Files

Pass multiple to DropZone.Input and collect files from both the picker (onSelect) and drag-and-drop (onDrop).

"use client";

import {DropZone} from "@darkcode-ui/react";
import React from "react";

Custom Icon

Provide children to DropZone.Icon to replace the default cloud upload icon.

import {DropZone} from "@darkcode-ui/react";

export function CustomIcon() {
  return (
    <DropZone className="w-full max-w-md">

Custom Triggers

The Trigger, retry, and remove buttons all accept className and custom children for full control over their appearance.

import {DropZone} from "@darkcode-ui/react";

export function CustomTriggers() {
  return (
    <DropZone className="w-full max-w-md">

useDropZone Hook

The useDropZone hook manages the whole upload lifecycle — file state, validation, image previews, and uploads — so you only render the UI. Spread getAreaProps() onto DropZone.Area and getInputProps() onto DropZone.Input, then map files to DropZone.FileItem. Rejections (wrong type, too large, too many) are reported via onFilesRejected.

"use client";

import {DropZone, formatFileSize, useDropZone} from "@darkcode-ui/react";
import React from "react";

Image Previews

DropZone.FilePreview renders an image thumbnail from a File or a URL string, with automatic object-URL cleanup and a graceful fallback (a file format icon) for non-images or load errors. useDropZone populates file.preview for images automatically.

"use client";

import {DropZone, formatFileSize, useDropZone} from "@darkcode-ui/react";

export function ImagePreview() {

Upload Progress

Pass onUpload (or uploadUrl) to useDropZone to upload each accepted file with live progress. Failed uploads surface a retry, and removing a file aborts its upload. For real uploads, use the exported uploadFile helper — it is XMLHttpRequest-based, so it reports true progress and supports AbortSignal.

"use client";

import type {DropZoneFile, UseDropZoneUploadHelpers} from "@darkcode-ui/react";

import {DropZone, formatFileSize, useDropZone} from "@darkcode-ui/react";

Directory & Paste

Set acceptDirectory on DropZone.Input to select an entire folder (webkitdirectory), or capture for the mobile camera. Clipboard paste (⌘/Ctrl + V) is supported automatically when the area is focused — pasted files flow through the same onDrop handler.

"use client";

import {DropZone, formatFileSize, useDropZone} from "@darkcode-ui/react";

function badge(name: string): {color: string; format: string} {

Styling

Customizing the component classes

To customize the DropZone component classes, you can use the @layer components directive.
Learn more.

@layer components {
  .drop-zone__area {
    @apply rounded-xl border-foreground/20;
  }

  .drop-zone__file-item {
    @apply bg-default/40;
  }
}

DarkCode UI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The DropZone component uses these CSS classes (View source styles):

Base Classes

  • .drop-zone - Root wrapper

Element Classes

  • .drop-zone__area - The droppable area (RAC DropZone)
  • .drop-zone__icon - Upload icon container
  • .drop-zone__label - Primary label text
  • .drop-zone__description - Secondary description text
  • .drop-zone__trigger - Browse button
  • .drop-zone__input - Hidden file input
  • .drop-zone__file-list - File list container with animation
  • .drop-zone__file-item - Individual file entry
  • .drop-zone__file-format-icon - File type SVG icon with colored badge
  • .drop-zone__file-info - File name and metadata container
  • .drop-zone__file-name - File name text
  • .drop-zone__file-meta - File size/status metadata
  • .drop-zone__file-progress - Upload progress bar
  • .drop-zone__file-retry-trigger - Retry button for failed uploads
  • .drop-zone__file-remove-trigger - Remove/close button

Modifier Classes

  • .drop-zone__file-list--compact - Denser file list layout
  • .drop-zone__file-format-icon--{color} - Badge color (gray, red, orange, amber, green, blue, purple)

Interactive States

  • Drop target: [data-drop-target="true"] on .drop-zone__area (accent border and background)
  • Focus visible: [data-focus-visible="true"] on .drop-zone__area (focus ring)
  • Disabled: [data-disabled="true"] on .drop-zone__area (reduced opacity)
  • File status: [data-status="uploading|complete|failed"] on .drop-zone__file-item

API Reference

DropZone

The root wrapper component. Provides file picker context.

PropTypeDefaultDescription
childrenReactNode-Area, FileList, and other sub-components
classNamestring-Additional CSS class
renderDOMRenderFunction-Custom render function to override the default div element

Also supports all native div HTML attributes.

DropZone.Area

The droppable area. Wraps RAC DropZone.

Also supports all RAC DropZone props (onDrop, getDropOperation, isDisabled, etc.).

DropZone.Icon

Upload icon container. Renders a default cloud upload icon when no children are provided.

DropZone.Label

Primary label text. Wraps RAC Text with slot="label".

DropZone.Description

Secondary description text.

DropZone.Input

Hidden file input for the browse trigger.

PropTypeDefaultDescription
acceptstring-Accepted file types (e.g. "image/*,.pdf")
multipleboolean-Whether multiple files can be selected
acceptDirectoryboolean-Allow selecting an entire directory (sets webkitdirectory)
captureboolean | "user" | "environment"-Mobile capture mode for the camera/microphone
onSelect(files: FileList) => void-Called when files are selected via the file picker

DropZone.Trigger

Browse button that opens the file picker. Wraps RAC Button. When no children are provided, it renders Select files.

DropZone.FileList

Animated file list container using Motion AnimatePresence.

PropTypeDefaultDescription
compactbooleanfalseRenders a denser layout for the file rows

DropZone.FileItem

Individual file entry with Motion layout animations.

PropTypeDefaultDescription
status'uploading' | 'complete' | 'failed'-Upload status of this file item

DropZone.FileFormatIcon

File type SVG icon with a colored format badge.

PropTypeDefaultDescription
formatstring-File format label (e.g. "PDF", "JPG")
colorstring'gray'Badge color (gray, red, orange, amber, green, blue, purple)

DropZone.FilePreview

Image thumbnail for a file. Renders an <img> from a File (creating and revoking an object URL automatically) or a URL string, and falls back to a file format icon for non-images or load errors.

PropTypeDefaultDescription
srcFile | string-A File (object URL is managed for you) or an image URL
fallbackReactNode<DropZone.FileFormatIcon />Rendered when there is no preview or the image fails to load

Also supports all native img HTML attributes.

DropZone.FileInfo

Container for file name and metadata.

DropZone.FileName

File name text display.

DropZone.FileMeta

File size/status metadata text.

DropZone.FileProgress

Upload progress bar. Wraps DarkCode UI ProgressBar.

PropTypeDefaultDescription
sizestring'sm'Progress bar size

Also supports all ProgressBar props.

DropZone.FileProgressTrack

Progress bar track. Wraps ProgressBar.Track.

DropZone.FileProgressFill

Progress bar fill. Wraps ProgressBar.Fill.

DropZone.FileRetryTrigger

Retry button for failed uploads. Wraps RAC Button.

DropZone.FileRemoveTrigger

Remove/close button. Wraps RAC Button.

Hooks & helpers

useDropZone

A headless hook that manages file state, validation, image previews, and uploads. Returns the file list and prop getters to wire onto the presentational parts.

import { useDropZone } from '@darkcode-ui/react';

const dz = useDropZone({
  accept: 'image/*,.pdf',
  maxSize: 5 * 1024 * 1024,
  maxFiles: 5,
  onUpload: (file, { onProgress, signal }) => upload(file, onProgress, signal),
});

// <DropZone.Area {...dz.getAreaProps()}>
// <DropZone.Input {...dz.getInputProps()} />
// dz.files.map(...) → dz.removeFile(id), dz.retryFile(id)

Options

OptionTypeDefaultDescription
filesDropZoneFile[]-Controlled file state
defaultFilesDropZoneFile[][]Initial (uncontrolled) file state
onFilesChange(files: DropZoneFile[]) => void-Called whenever the file list changes
acceptstring-Accepted file types (same syntax as the input accept)
maxSizenumber-Maximum size per file, in bytes
minSizenumber-Minimum size per file, in bytes
maxFilesnumber-Maximum number of files allowed in total
multiplebooleanmaxFiles !== 1Whether multiple files can be selected
validate(file: File) => string | null-Custom validator; return a message to reject
disabledbooleanfalseDisable selection, drop, and validation
generatePreviewsbooleantrueCreate object-URL previews for image files
onFilesAccepted(files: DropZoneFile[]) => void-Called with the files that were added
onFilesRejected(rejections: DropZoneRejection[]) => void-Called with the files that failed validation
onUpload(file, helpers) => Promise-Upload implementation per file (helpers: onProgress, signal)
uploadUrlstring-Convenience endpoint; uploads via uploadFile when onUpload is not set
uploadOptionsUploadFileOptions-Extra options forwarded to uploadFile when uploadUrl is used

Returns

PropertyTypeDescription
filesDropZoneFile[]Current files
isDisabledbooleanWhether the drop zone is disabled
totalSizenumberCombined byte size of all files
addFiles(files: FileList | File[]) => voidValidate and add files
removeFile(id: string) => voidRemove a file (aborts upload, revokes preview)
retryFile(id: string) => voidRetry a failed/aborted upload
updateFile(id: string, patch: Partial<DropZoneFile>) => voidPatch a single file entry
clear() => voidRemove all files
getInputProps() => objectProps to spread onto DropZone.Input
getAreaProps() => objectProps to spread onto DropZone.Area (also enables paste)

DropZoneFile is { id, file, name, size, type, status, progress, error?, preview? }, where status is 'pending' | 'uploading' | 'complete' | 'failed'.

uploadFile

XMLHttpRequest-based upload helper with real progress events and abort support. Pass it to useDropZone's onUpload, or call it directly.

import { uploadFile } from '@darkcode-ui/react';

await uploadFile(file, {
  url: '/api/upload',
  onProgress: ({ percent }) => setProgress(percent ?? 0),
  signal: controller.signal,
});
OptionTypeDefaultDescription
urlstring-Endpoint the file is uploaded to
methodstring"POST"HTTP method
fieldNamestring"file"Form field name for the file part
headersRecord<string, string>-Additional request headers
dataRecord<string, string | Blob>-Extra fields appended to the FormData
withCredentialsbooleanfalseSend cookies/credentials cross-origin
signalAbortSignal-Cancels the in-flight upload
onProgress(progress) => void-Progress callback ({ loaded, total, percent })

formatFileSize

formatFileSize(bytes, decimals?) formats a byte count into a human-readable string (e.g. 2.4 MB).

On this page