Simple HTML5 drag-drop zone with React.js
npx @tessl/cli install tessl/npm-react-dropzone@14.3.0React Dropzone is a React library that provides simple HTML5-compliant drag-and-drop zones for file uploads. It offers a powerful useDropzone hook and convenient Dropzone component wrapper with extensive customization options, file validation, and comprehensive event handling.
npm install react-dropzone or yarn add react-dropzoneimport Dropzone, { useDropzone, ErrorCode, FileWithPath } from "react-dropzone";For CommonJS:
const Dropzone = require("react-dropzone").default;
const { useDropzone, ErrorCode, FileWithPath } = require("react-dropzone");import React, { useCallback } from "react";
import { useDropzone } from "react-dropzone";
function MyDropzone() {
const onDrop = useCallback((acceptedFiles: File[]) => {
// Process the dropped files
console.log(acceptedFiles);
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
{isDragActive ? (
<p>Drop the files here...</p>
) : (
<p>Drag 'n' drop files here, or click to select files</p>
)}
</div>
);
}React Dropzone is built around modern React patterns:
useDropzone hook (React 16.8+)Dropzone component for render prop patternThe primary interface for creating drag-and-drop zones with full customization options.
/**
* Creates a drag 'n' drop area with comprehensive configuration options
* @param options - Configuration options for the dropzone
* @returns State object with drag states, file arrays, and prop getter functions
*/
function useDropzone(options?: DropzoneOptions): DropzoneState;
interface DropzoneOptions {
/** Accepted file types as MIME type object (default: undefined - all files) */
accept?: Accept;
/** Allow multiple file selection (default: true) */
multiple?: boolean;
/** Prevent drops on document (default: true) */
preventDropOnDocument?: boolean;
/** Disable click to open file dialog (default: false) */
noClick?: boolean;
/** Disable keyboard file dialog triggers (default: false) */
noKeyboard?: boolean;
/** Disable drag and drop (default: false) */
noDrag?: boolean;
/** Stop drag event bubbling (default: false) */
noDragEventsBubbling?: boolean;
/** Minimum file size in bytes (default: 0) */
minSize?: number;
/** Maximum file size in bytes (default: Infinity) */
maxSize?: number;
/** Maximum number of files (default: 0 - unlimited) */
maxFiles?: number;
/** Disable the dropzone (default: false) */
disabled?: boolean;
/** Use File System Access API (default: false) */
useFsAccessApi?: boolean;
/** Auto focus the dropzone (default: false) */
autoFocus?: boolean;
/** Custom file aggregator function (default: fromEvent from file-selector) */
getFilesFromEvent?: (event: DropEvent) => Promise<Array<File | DataTransferItem>>;
/** Custom file validator (default: null) */
validator?: <T extends File>(file: T) => FileError | readonly FileError[] | null;
/** Callback when files are dropped */
onDrop?: <T extends File>(
acceptedFiles: T[],
fileRejections: FileRejection[],
event: DropEvent
) => void;
/** Callback when files are accepted */
onDropAccepted?: <T extends File>(files: T[], event: DropEvent) => void;
/** Callback when files are rejected */
onDropRejected?: (fileRejections: FileRejection[], event: DropEvent) => void;
/** Callback when drag enters */
onDragEnter?: (event: React.DragEvent<HTMLElement>) => void;
/** Callback when drag leaves */
onDragLeave?: (event: React.DragEvent<HTMLElement>) => void;
/** Callback when dragging over */
onDragOver?: (event: React.DragEvent<HTMLElement>) => void;
/** Callback when file dialog opens */
onFileDialogOpen?: () => void;
/** Callback when file dialog is cancelled */
onFileDialogCancel?: () => void;
/** Error handler */
onError?: (error: Error) => void;
}
interface DropzoneState {
/** Dropzone is focused */
isFocused: boolean;
/** Active drag is in progress */
isDragActive: boolean;
/** Dragged files are accepted */
isDragAccept: boolean;
/** Some dragged files are rejected */
isDragReject: boolean;
/** File dialog is open */
isFileDialogActive: boolean;
/** Array of accepted files */
acceptedFiles: readonly FileWithPath[];
/** Array of rejected files with errors */
fileRejections: readonly FileRejection[];
/** Reference to root element */
rootRef: React.RefObject<HTMLElement>;
/** Reference to input element */
inputRef: React.RefObject<HTMLInputElement>;
/** Get props for root container */
getRootProps: <T extends DropzoneRootProps>(props?: T) => T;
/** Get props for hidden file input */
getInputProps: <T extends DropzoneInputProps>(props?: T) => T;
/** Programmatically open file dialog */
open: () => void;
}Usage Examples:
import { useDropzone } from "react-dropzone";
// Basic file upload
const basic = useDropzone({
onDrop: (files) => console.log("Dropped files:", files),
});
// With file type restrictions
const imageOnly = useDropzone({
accept: {
"image/*": [".jpeg", ".jpg", ".png", ".gif"],
},
onDrop: (files) => uploadImages(files),
});
// With size limits and multiple files
const documents = useDropzone({
accept: {
"application/pdf": [".pdf"],
"application/msword": [".doc", ".docx"],
},
minSize: 1024, // 1KB minimum
maxSize: 5242880, // 5MB maximum
maxFiles: 3,
onDropAccepted: (files) => handleAccepted(files),
onDropRejected: (rejections) => handleRejected(rejections),
});Convenience wrapper component using render prop pattern for the useDropzone hook.
/**
* Convenience wrapper component for the useDropzone hook
* @param props - Dropzone options plus children render function
*/
function Dropzone(
props: DropzoneProps & React.RefAttributes<DropzoneRef>
): React.ReactElement;
interface DropzoneProps extends DropzoneOptions {
/** Render function that receives dropzone state */
children?(state: DropzoneState): React.ReactElement;
}
interface DropzoneRef {
/** Programmatically open file dialog */
open: () => void;
}Usage Examples:
import Dropzone from "react-dropzone";
// Basic usage with render prop
<Dropzone onDrop={(files) => console.log(files)}>
{({ getRootProps, getInputProps, isDragActive }) => (
<div {...getRootProps()}>
<input {...getInputProps()} />
{isDragActive ? (
<p>Drop files here...</p>
) : (
<p>Drag files here or click to browse</p>
)}
</div>
)}
</Dropzone>
// With imperative access via ref
const dropzoneRef = useRef<DropzoneRef>(null);
<Dropzone ref={dropzoneRef} onDrop={handleDrop}>
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()}>
<input {...getInputProps()} />
<button onClick={() => dropzoneRef.current?.open()}>
Browse Files
</button>
</div>
)}
</Dropzone>Built-in validation system with detailed error reporting for rejected files.
/** Error codes for file validation failures */
enum ErrorCode {
FileInvalidType = "file-invalid-type",
FileTooLarge = "file-too-large",
FileTooSmall = "file-too-small",
TooManyFiles = "too-many-files",
}
interface FileError {
/** Human-readable error message */
message: string;
/** Error code for programmatic handling */
code: ErrorCode | string;
}
interface FileRejection {
/** The rejected file */
file: FileWithPath;
/** Array of validation errors */
errors: readonly FileError[];
}
interface FileWithPath extends File {
/** File path (when available) */
path?: string;
}Usage Examples:
import { useDropzone, ErrorCode } from "react-dropzone";
const { getRootProps, getInputProps, fileRejections } = useDropzone({
accept: { "image/*": [] },
maxSize: 1048576, // 1MB
onDropRejected: (rejections) => {
rejections.forEach(({ file, errors }) => {
console.log(`File ${file.name} rejected:`);
errors.forEach((error) => {
switch (error.code) {
case ErrorCode.FileInvalidType:
console.log("Invalid file type");
break;
case ErrorCode.FileTooLarge:
console.log("File too large");
break;
default:
console.log(error.message);
}
});
});
},
});
// Custom validator
const customValidation = useDropzone({
validator: (file) => {
if (file.name.includes("temp")) {
return {
code: "name-not-allowed",
message: "Temporary files are not allowed",
};
}
return null;
},
});Advanced features for specialized use cases and enhanced user experience.
interface DropzoneRootProps extends React.HTMLAttributes<HTMLElement> {
/** Reference key for root element (default: "ref") */
refKey?: string;
[key: string]: any;
}
interface DropzoneInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
/** Reference key for input element (default: "ref") */
refKey?: string;
}
interface Accept {
/** MIME type keys with array of file extensions */
[mimeType: string]: readonly string[];
}
type DropEvent =
| React.DragEvent<HTMLElement>
| React.ChangeEvent<HTMLInputElement>
| DragEvent
| Event
| Array<FileSystemFileHandle>;Usage Examples:
// File System Access API (modern browsers)
const modernFileAccess = useDropzone({
useFsAccessApi: true,
onDrop: (files) => console.log("Files selected:", files),
});
// Custom file processing
const customProcessor = useDropzone({
getFilesFromEvent: async (event) => {
// Custom file extraction logic
const items = Array.from(event.dataTransfer?.items || []);
return items
.filter((item) => item.kind === "file")
.map((item) => item.getAsFile())
.filter(Boolean);
},
});
// Preventing document drops globally
const secureDropzone = useDropzone({
preventDropOnDocument: true,
noDragEventsBubbling: true,
onDrop: (files) => secureUpload(files),
});
// Accessibility and keyboard support
const accessibleDropzone = useDropzone({
autoFocus: true,
noKeyboard: false, // Allow SPACE/ENTER to open dialog
onDrop: (files) => handleFiles(files),
});/** File with optional path information */
interface FileWithPath extends File {
path?: string;
}
/** Configuration for accepted file types */
interface Accept {
[mimeType: string]: readonly string[];
}
/** Union type for various drop events */
type DropEvent =
| React.DragEvent<HTMLElement>
| React.ChangeEvent<HTMLInputElement>
| DragEvent
| Event
| Array<FileSystemFileHandle>;
/** Error codes for file validation */
enum ErrorCode {
FileInvalidType = "file-invalid-type",
FileTooLarge = "file-too-large",
FileTooSmall = "file-too-small",
TooManyFiles = "too-many-files",
}
/** File validation error */
interface FileError {
message: string;
code: ErrorCode | string;
}
/** Rejected file with associated errors */
interface FileRejection {
file: FileWithPath;
errors: readonly FileError[];
}