Build a Multiple Image or File Uploader with Preview Feature Using React
By Bytewebster - June 26, 2023
In this tutorial, you will learn how you can easily create a multiple file and image uploader in React JS.
Along with this, this uploader will also show you the preview of those images with their details.
And also, you will get a delete option and a download option for every selected image.
Working
This image uploader gives you the functionality of drag and drop files and images so that you can upload images easily. Once your selected images are uploaded, immediately after that, your images will be shown under this uploader. In which you can also remove the images, and if you want, you can also download those images.
Detailed Overview of Project
This multiple image uploader is not completely made in React; if you want, you can use it without a React App By using the import statements. The code of this multiple file uploader is pulling the necessary dependencies from the CDN (Content delivery network) URLs, allowing the usage of React and ReactDOM in the current JavaScript file. Let's start with the HTML part first.
HTML Structure
As you can see, there is nothing much in its HTML part. There's a div element with the id attribute set to "root" and the class "mt-5". This is the root element where the Multiple Image/File Uploader will be rendered.
In React, the root is a placeholder element commonly used as the root container for rendering React components into the DOM (Document Object Model).
And in its head tag, the CDN links of Bootstrap are placed. If you want, you can also remove them; it will not make any difference in its design.
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Multiple Images Upload with Preview Using React - ByteWebster</title>
<link rel="stylesheet" href="./style.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div id="root" class="mt-5"></div>
<script type="module" src="./script.js"></script>
</body>
</html>
Styling With CSS
Let's move on to designing it using CSS. In the first few lines, import external CSS files from Bootstrap, Font Awesome, and Google Fonts to use their styles and icons.
Next, we set the background image of the body to a linear gradient and applied a specific font family, size, weight, and colour to the text.
The Main file or image uploader component is styled with many CSS properties, Like background colour, border radius, and some other properties. Next, we also include styles for the file upload input and file link.
@import url(https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css);
@import url(https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css);
@import url("https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700;800;900&display=swap");
body {
background-image: linear-gradient(to right, #6a11cb 0%, #2575fc 100%);
font-family: "Rubik", sans-serif;
font-size: 15px;
font-weight: 400;
line-height: 24px;
color: #727E8C;
overflow-y: scroll;
}
body::-webkit-scrollbar {
width: 0em;
}
body::-webkit-scrollbar-track {
background-color: transparent;
}
body::-webkit-scrollbar-thumb {
background-color: transparent;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
color: #475F7B;
}
a, a:hover, a:focus {
text-decoration: none;
transition: 0.5s;
outline: none;
}
.card {
box-shadow: 0px 4px 8px rgb(0 0 0 / 16%);
border-radius: 6px;
overflow: hidden;
margin-bottom: 30px;
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
background-color: #fff;
border: none;
}
.card-body {
padding: 30px;
}
.btn-primary {
border-color: #5a8dee !important;
background-color: #5a8dee !important;
color: #fff;
}
.form-submit{
padding: 13px 30px;
font-size: 15px;
letter-spacing: 0.3px;
font-weight: 400;
}
.kb-data-box {
width: 100%;
flex: 1;
}
.kb-modal-data-title {
margin-bottom: 50px;
}
.kb-data-title h6 {
margin-bottom: 0;
font-size: 15px;
font-weight: 600;
}
.kb-file-upload {
margin-bottom: 50px;
}
.file-upload-box {
border: 1px dashed #b6bed1;
background-color: #f0f2f7;
border-radius: 4px;
min-height: 150px;
position: relative;
overflow: hidden;
padding: 15px;
display: flex;
align-items: center;
justify-content: center;
color: #8194aa;
font-weight: 400;
font-size: 15px;
}
.file-upload-box .file-upload-input {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
cursor: pointer;
}
.file-link{
color: #475f7b;
text-decoration: underline;
margin-left: 3px;
}
.file-upload-box .file-link:hover{
text-decoration: none;
}
.file-atc-box {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.file-image {
/* width: 130px;
min-width: 130px;
height: 85px;
min-height: 85px; */
width: 130px;
height: 85px;
background-size: cover;
border-radius: 5px;
margin-right: 15px;
background-color: #eaecf1;
display: flex;
align-items: center;
justify-content: center;
font-size: 30px;
color: #475f7b;
padding: 3px;
}
.file-image img{
max-width: 100%;
max-height: 100%;
border-radius: 4px;
}
.file-detail {
flex: 1;
width: calc(100% - 210px);
}
.file-detail h6 {
word-break: break-all;
font-size: 13px;
font-weight: 500;
line-height: 20px;
margin-bottom: 8px;
}
.file-detail p {
font-size: 12px;
color: #8194aa;
line-height: initial;
font-weight: 400;
margin-bottom: 8px;
}
.file-actions {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
align-items: center;
}
.file-action-btn {
font-size: 12px;
color: #8194aa;
line-height: 20px;
font-weight: 400;
margin-bottom: 0;
padding: 0;
background-color: transparent;
border: none;
text-decoration: underline;
margin-right: 15px;
cursor: pointer;
}
.file-action-btn:hover {
color: #3d546f;
text-decoration: underline;
}
.file-atc-box:last-child{
margin-bottom: 0;
}
After all this, we styled the file or image details section. The file or image details section includes styles for the file image, file name, and other information.
And last, it has sections of actions for files and images. The file actions section includes styles for buttons or links associated with each file, such as delete or download options. The rest of the code is given above for your reference.
JavaScript Explanation
Now come to the most important part, because of which this multiple file uploader has become possible. So let's know how it is working step by step.
First of all, we have imported the necessary dependencies using URLs from a CDN like React, ReactDOM, and shortid. You will find all those CDN links below.After importing them, we defined a component function called 'App' using the arrow function syntax. It initialises two states The first one is selectedfile to store the selected files for preview, and Files to store the uploaded files and images.
Then we use the filesizes function to convert file sizes from bytes to a more readable format (e.g., KB, MB, or GB). Also, The InputChange function is called when the file input's value changes. It loops through the selected files and images.
import React, { useState } from "https://cdn.skypack.dev/react@17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import shortid from "https://cdn.skypack.dev/shortid@2.2.16";
const App = () => {
const [selectedfile, SetSelectedFile] = useState([]);
const [Files, SetFiles] = useState([]);
const filesizes = (bytes, decimals = 2) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
};
const InputChange = e => {
// --For Multiple File Input
let images = [];
for (let i = 0; i < e.target.files.length; i++) {
images.push(e.target.files[i]);
let reader = new FileReader();
let file = e.target.files[i];
reader.onloadend = () => {
SetSelectedFile(preValue => {
return [
...preValue,
{
id: shortid.generate(),
filename: e.target.files[i].name,
filetype: e.target.files[i].type,
fileimage: reader.result,
datetime: e.target.files[i].lastModifiedDate.toLocaleString('en-IN'),
filesize: filesizes(e.target.files[i].size) }];
});
};
if (e.target.files[i]) {
reader.readAsDataURL(file);
}
}
};
const DeleteSelectFile = id => {
if (window.confirm("Are you sure you want to delete this Image?")) {
const result = selectedfile.filter(data => data.id !== id);
SetSelectedFile(result);
} else {
// alert('No');
}
};
const FileUploadSubmit = async e => {
e.preventDefault();
// form reset on submit
e.target.reset();
if (selectedfile.length > 0) {
for (let index = 0; index < selectedfile.length; index++) {
SetFiles(preValue => {
return [
...preValue,
selectedfile[index]];
});
}
SetSelectedFile([]);
} else {
alert('Please select atlease one image!');
}
};
const DeleteFile = async id => {
if (window.confirm("Are you sure you want to delete this Image?")) {
const result = Files.filter(data => data.id !== id);
SetFiles(result);
} else {
// alert('No');
}
};
return (
React.createElement("div", { className: "fileupload-view" },
React.createElement("div", { className: "row justify-content-center m-0" },
React.createElement("div", { className: "col-md-6" },
React.createElement("div", { className: "card mt-5" },
React.createElement("div", { className: "card-body" },
React.createElement("div", { className: "kb-data-box" },
React.createElement("div", { className: "kb-modal-data-title" },
React.createElement("div", { className: "kb-data-title" },
React.createElement("h5", null, "Multiple Images Upload with Preview Using React"))),
React.createElement("form", { onSubmit: FileUploadSubmit },
React.createElement("div", { className: "kb-file-upload" },
React.createElement("div", { className: "file-upload-box" },
React.createElement("input", { type: "file", id: "fileupload", className: "file-upload-input", onChange: InputChange, multiple: true }),
React.createElement("span", null, "Drag and drop or ", React.createElement("span", { className: "file-link" }, "Choose your files or images")))),
React.createElement("div", { className: "kb-attach-box mb-3" },
selectedfile.map((data, index) => {
const { id, filename, filetype, fileimage, datetime, filesize } = data;
return (
React.createElement("div", { className: "file-atc-box", key: id },
filename.match(/.(jpg|jpeg|png|gif|svg)$/i) ?
React.createElement("div", { className: "file-image" }, " ", React.createElement("img", { src: fileimage, alt: "" })) :
React.createElement("div", { className: "file-image" }, React.createElement("i", { className: "far fa-file-alt" })),
React.createElement("div", { className: "file-detail" },
React.createElement("h6", null, filename),
React.createElement("p", null),
React.createElement("p", null, React.createElement("span", null, "Size : ", filesize), React.createElement("span", { className: "ml-2" }, "Modified Time : ", datetime)),
React.createElement("div", { className: "file-actions" },
React.createElement("button", { type: "button", className: "file-action-btn", onClick: () => DeleteSelectFile(id) }, "Delete")))));
})),
React.createElement("div", { className: "kb-buttons-box" },
React.createElement("button", { type: "submit", className: "btn btn-primary form-submit" }, "Upload Images"))),
Files.length > 0 ?
React.createElement("div", { className: "kb-attach-box" },
React.createElement("hr", null),
Files.map((data, index) => {
const { id, filename, filetype, fileimage, datetime, filesize } = data;
return (
React.createElement("div", { className: "file-atc-box", key: index },
filename.match(/.(jpg|jpeg|png|gif|svg)$/i) ?
React.createElement("div", { className: "file-image" }, " ", React.createElement("img", { src: fileimage, alt: "" })) :
React.createElement("div", { className: "file-image" }, React.createElement("i", { className: "far fa-file-alt" })),
React.createElement("div", { className: "file-detail" },
React.createElement("h6", null, filename),
React.createElement("p", null, React.createElement("span", null, "Size : ", filesize), React.createElement("span", { className: "ml-3" }, "Modified Time : ", datetime)),
React.createElement("div", { className: "file-actions" },
React.createElement("button", { className: "file-action-btn", onClick: () => DeleteFile(id) }, "Delete"),
React.createElement("a", { href: fileimage, className: "file-action-btn", download: filename }, "Download")))));
})) :
'')))))));
};
ReactDOM.render( React.createElement(App, null), document.getElementById("root"));
Next, the DeleteSelectFile function is called when a user wants to delete a selected file from the preview. It removes the file from the selectedfile state based on its ID. Also The component's render function returns a JSX structure that creates the file uploader UI. It uses React.createElement to create the HTML elements with their respective attributes and event handlers.
Last The App component is rendered using ReactDOM.render, and it is attached to the HTML element with the ID "root".
We are grateful for your time and attention, and we trust that you have found the project to be interesting.
Video of the Project
Take This Short Survey!
Download Source Code Files
From here You can download the source code file of this React Multiple Image Uploader.
If you are just starting in web development, these snippets will be useful. We would appreciate it if you would share our blog posts with other like-minded people.
ByteWebster Play and Win Offer.
PLAY A SIMPLE GAME AND WIN PREMIUM WEB DESIGNS WORTH UPTO $100 FOR FREE.
PLAY FOR FREE