Drag and Drop File Upload Component with React Hooks
Last Updated :
24 Apr, 2025
In this article, we're going to delve into creating a drag-and-drop feature for uploading files using React Hooks. Dragging and dropping makes it simple for users to add files to a web app. With React Hooks we can control state and execute actions in components simplifying the management of drag and drop functions. By the conclusion of this guide, you'll have an operational drag-and-drop file upload component ready for integration, into your React projects.
Output Preview: Let us have a look at how the final output will look like.
Final outputPrerequisites
Approach to build drag and drop File Upload Component:
- Utilize drop features with HTML5 drag and drop events
- Manage file selection and upload by leveraging the FileReader API along, with AJAX requests.
- Show previews of files using the File API. Incorporate design elements and visual cues to improve user interaction.
Steps to Create the Application:
Step 1: Create a new project by following command
npx create-react-app drag-drop
Step 2: Navigate to the root directory of your project.
cd drag-drop
Folder Structure:
StructureExample: Make two files titled Drop-file-input.css and DropFileInput.jsx and place them in a new directory called components in the source folder.
CSS
/* drop-file-input.css */
.drop-file-input {
position: relative;
width: 800px;
height: 400px;
border: 4px solid var(--border-color);
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--input-bg);
}
.drop-file-input input {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.drop-file-input:hover,
.drop-file-input.dragover {
opacity: 0.6;
}
.drop-file-input__label {
text-align: center;
color: var(--txt-second-color);
font-weight: 600;
padding: 10px;
}
.drop-file-input__label img {
width: 100px;
}
.drop-file-preview {
margin-top: 30px;
}
.drop-file-preview p {
font-weight: 500;
}
.drop-file-preview__title {
margin-bottom: 20px;
}
.drop-file-preview__item {
position: relative;
display: flex;
margin-bottom: 10px;
background-color: var(--input-bg);
padding: 15px;
border-radius: 20px;
}
.drop-file-preview__item img {
width: 50px;
margin-right: 20px;
}
.drop-file-preview__item__info {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.drop-file-preview__item__del {
background-color: var(--box-bg);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
box-shadow: var(--box-shadow);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease;
}
.drop-file-preview__item:hover .drop-file-preview__item__del {
opacity: 1;
}
CSS
/* App.css */
@import url("https://fonts.googleapis.com/css2?family=Monospace:wght@300;400;500;600&display=swap");
:root {
--body-bg: #f5f8ff;
--box-bg: #fff;
--input-bg: #f5f8ff;
--txt-color: #2f2d2f;
--txt-second-color: #ccc;
--border-color: #31c503;
--box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
font-family: "Monospace", sans-serif;
font-weight: 400;
line-height: 1.5;
background-color: var(--body-bg);
color: var(--txt-color);
display: flex;
justify-content: center;
padding-top: 100px;
height: 100vh;
}
.box {
background-color: var(--box-bg);
padding: 30px;
border-radius: 20px;
box-shadow: var(--box-shadow);
}
.header {
margin-bottom: 30px;
text-align: center;
}
JavaScript
//App.js
import './App.css';
import DropFileInput from './components/drop-file-input/DropFileInput.js';
function App() {
const onFileChange = (files) => {
console.log(files);
}
return (
<div className="box">
<h2 className="header">
React drop files input
</h2>
<DropFileInput
onFileChange={(files) => onFileChange(files)}
/>
</div>
);
}
export default App;
JavaScript
// ImageConfig.js
export const ImageConfig = {
default: "https://media.geeksforgeeks.org/wp-content/uploads/20240307210250/file-blank-solid-240.png",
pdf: "https://media.geeksforgeeks.org/wp-content/uploads/20240307210251/file-pdf-solid-240.png",
png:"https://media.geeksforgeeks.org/wp-content/uploads/20240307210251/file-png-solid-240.png",
css: "https://media.geeksforgeeks.org/wp-content/uploads/20240307210251/file-css-solid-240.png"
}
JavaScript
// DropFileInput.jsx
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import './drop-file-input.css';
import { ImageConfig } from '../../config/ImageConfig.js';
const DropFileInput = props => {
const wrapperRef = useRef(null);
const [fileList, setFileList] = useState([]);
const onDragEnter = () => wrapperRef.current.classList.add('dragover');
const onDragLeave = () => wrapperRef.current.classList.remove('dragover');
const onDrop = () => wrapperRef.current.classList.remove('dragover');
const onFileDrop = (e) => {
const newFile = e.target.files[0];
if (newFile) {
const updatedList = [...fileList, newFile];
setFileList(updatedList);
props.onFileChange(updatedList);
}
}
const fileRemove = (file) => {
const updatedList = [...fileList];
updatedList.splice(fileList.indexOf(file), 1);
setFileList(updatedList);
props.onFileChange(updatedList);
}
return (
<>
<div
ref={wrapperRef}
className="drop-file-input"
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDrop={onDrop}
>
<div className="drop-file-input__label">
<img src={"https://media.geeksforgeeks.org/wp-content/uploads/20240308113922/Drag-.png"}
alt="" />
<p>Drag & Drop your files here</p>
</div>
<input type="file" value="" onChange={onFileDrop} />
</div>
{
fileList.length > 0 ? (
<div className="drop-file-preview">
<p className="drop-file-preview__title">
Ready to upload
</p>
{
fileList.map((item, index) => (
<div key={index} className="drop-file-preview__item">
<img src={ImageConfig[item.type.split('/')[1]] ||
ImageConfig['default']} alt="" />
<div className="drop-file-preview__item__info">
<p>{item.name}</p>
<p>{item.size}B</p>
</div>
<span className="drop-file-preview__item__del"
onClick={() => fileRemove(item)}>
x
</span>
</div>
))
}
</div>
) : null
}
</>
);
}
DropFileInput.propTypes = {
onFileChange: PropTypes.func
}
export default DropFileInput;