Drag and Drop File Upload in React with react-dropzone
Build a polished drag-and-drop file upload component in React using react-dropzone. Covers MIME filtering, file previews, and upload progress with working code.
Building a smooth drag-and-drop file upload interface in React enhances usability and engagement. In this guide, we build an accessible, responsive dropzone component using react-dropzone with full file preview support and upload progress.
Installing react-dropzone
npm install react-dropzone lucide-react
Download a standard 10MB test PDF to test your React drag-and-drop dropzone component.
Download 10MB PDF Sample →Complete React Component
import React, { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { UploadCloud, FileText, CheckCircle2, AlertCircle } from 'lucide-react';
export function FileDropzone() {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const onDrop = useCallback((acceptedFiles: File[], fileRejections: any[]) => {
setErrorMessage(null);
if (fileRejections.length > 0) {
const error = fileRejections[0].errors[0];
setErrorMessage(error.message);
return;
}
if (acceptedFiles.length > 0) {
setSelectedFile(acceptedFiles[0]);
}
}, []);
const { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
onDrop,
maxFiles: 1,
maxSize: 20 * 1024 * 1024, // 20MB
accept: {
'application/pdf': ['.pdf'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
'image/*': ['.png', '.jpg', '.jpeg'],
},
});
return (
<div className="max-w-xl mx-auto my-8">
<div
{...getRootProps()}
className={`p-8 border-2 border-dashed rounded-2xl text-center cursor-pointer transition-colors ${
isDragActive
? 'border-blue-500 bg-blue-500/10'
: isDragReject
? 'border-red-500 bg-red-500/10'
: 'border-slate-700 bg-slate-900/50 hover:border-slate-600'
}`}
>
<input {...getInputProps()} />
<div className="flex flex-col items-center justify-center space-y-3">
<div className="p-3 bg-blue-500/10 rounded-xl text-blue-400">
<UploadCloud className="w-8 h-8" />
</div>
{isDragActive ? (
<p className="text-sm font-medium text-blue-400">Drop the file here...</p>
) : (
<div>
<p className="text-sm font-semibold text-white">
Click to upload or drag and drop
</p>
<p className="text-xs text-slate-400 mt-1">
PDF, DOCX, PNG, or JPG (max 20MB)
</p>
</div>
)}
</div>
</div>
{selectedFile && (
<div className="mt-4 p-4 rounded-xl bg-slate-900 border border-slate-800 flex items-center justify-between">
<div className="flex items-center space-x-3">
<FileText className="w-5 h-5 text-blue-400" />
<div>
<p className="text-sm font-medium text-white">{selectedFile.name}</p>
<p className="text-xs text-slate-400">
{(selectedFile.size / (1024 * 1024)).toFixed(2)} MB
</p>
</div>
</div>
<CheckCircle2 className="w-5 h-5 text-emerald-400" />
</div>
)}
{errorMessage && (
<div className="mt-4 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-xs flex items-center space-x-2">
<AlertCircle className="w-4 h-4 flex-shrink-0" />
<span>{errorMessage}</span>
</div>
)}
</div>
);
}
Dropzone UX Best Practices
- Visual feedback on drag over: Dim background or illuminate borders when a valid file is hovering.
- Clear rejection states: Show distinct red styling if an unsupported file format is dragged over.
- Keyboard navigation: Ensure users who rely on keyboards can press
Tabto focus the dropzone andEnterto open the native OS file picker.
Nguyen Dai Long
AuthorBackend Lead • Distributed Systems & Cloud Edge Architecture Specialist
4+ years designing high-throughput file ingestion pipelines, database architectures, and distributed edge storage on Cloudflare R2 & AWS S3. Founder of FileDummy and the NDL Ecosystem.
Was this article helpful?
Click Like to support the author and help other developers discover this guide.
Engineering Discussion & Feedback (0)
Share benchmark results, report edge cases, or ask technical questions.
No comments yet. Be the first developer to start the discussion!
Frequently Asked Questions
What is the difference between react-dropzone and the HTML5 drag-and-drop API?â–¾
react-dropzone wraps the HTML5 File API with an onDrop callback, MIME type filtering via the accept prop, maxSize validation, and accessible keyboard support — saving 100+ lines of boilerplate compared to raw HTML5 drag events.
How do I limit file types in react-dropzone?â–¾
Pass the accept prop as an object: { 'application/pdf': ['.pdf'], 'image/*': ['.jpg', '.png'] }. This is the react-dropzone v12+ API (breaking change from the old string format in v11).
Can react-dropzone upload files directly to S3?â–¾
react-dropzone only provides the File object — it does not handle uploads. Combine it with a presigned S3 PUT request or the tus-js-client library to upload directly from the browser to S3/R2 without a proxy server.