In this React tutorial, I will show you way to build React.js Multiple Images upload example with Preview using React Hooks (in functional component), Axios and Multipart File for making HTTP requests, Bootstrap for progress bar and display list of images’ information (with download url).
More Practice:
– Typescript: React Typescript Multiple Image Upload example (with Preview)
– Drag and Drop File Upload with React Hooks example
– React Hooks CRUD example with Axios and Web API
– React Form Validation with Hooks example
– React Hooks: JWT Authentication (without Redux) example
– React + Redux: JWT Authentication example
– React Custom Hook
Contents
- Multiple Image upload in React.js Overview
- Technology
- Rest API for Image Upload & Storage
- Project Structure
- Setup React Multiple Image Upload Project
- Import Bootstrap to React.js Multiple Images Upload Project
- Initialize Axios for React HTTP Client
- Create Service for File Upload
- Create Functional Component for Upload Multiple Images
- Add Multiple Images Upload Functional Component to App Component
- Configure Port for React.js Multiple Image Upload App
- Run the App
- Conclusion
- Source Code
- Further Reading
Multiple Image upload in React.js Overview
We’re gonna create a React.js functional component Multiple Images Upload with Preview application in that user can:
- see the preview of images that will be uploaded
- see the upload process (percentage) of each image with progress bars
- view all uploaded files
- download link to file when clicking on the file name
Here are screenshots of our React App:
– Before uploading multiple Images, we can see the preview:

– Upload is processed and finished with Progress bars:

– List of Images Display with download Urls:

– Show status for each image upload:

Technology
- React 18/17/16
- Axios 0.27.2
- Bootstrap 4
Rest API for Image Upload & Storage
Here are APIs that we will use Axios to make HTTP requests:
| Methods | Urls | Actions |
|---|---|---|
| POST | /upload | upload a File |
| GET | /files | get List of Files (name & url) |
| GET | /files/[filename] | download a File |
You can find how to implement the Rest APIs Server at one of following posts:
– Node.js Express File Upload Rest API example
– Node.js Express File Upload to MongoDB example
– Node.js Express File Upload to Google Cloud Storage example
– Spring Boot Multipart File upload (to static folder) example
Project Structure
After building the React project is done, the folder structure will look like this:

Let me explain it briefly.
– FileUploadService provides methods to save File and get Files using Axios.
– ImagesUpload is functional components that contains upload form for multiple images, preview, progress bar, list of uploaded images display.
– App.js is the container that we embed all React functional components and Bootstrap CSS.
– http-common.js initializes Axios with HTTP base Url and headers.
– We configure port for our App in .env
Setup React Multiple Image Upload Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app multiple-image-upload-react-js
Or: yarn create react-app multiple-image-upload-react-js
After the process is done. We create additional folders and files like the following tree:
public
src
components
ImagesUpload.js
services
FileUploadService.js
App.css
App.js
index.js
package.json
Import Bootstrap to React.js Multiple Images Upload Project
Run command: npm install [email protected]
Or: yarn add [email protected]
Open src/App.js and modify the code inside it as following-
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
function App() {
return (
...
);
}
export default App;
Initialize Axios for React HTTP Client
Let’s install axios with command: npm install [email protected].
Or: yarn add [email protected].
Under src folder, we create http-common.js file with following code:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080",
headers: {
"Content-type": "application/json"
}
});
You can change the baseURL that depends on REST APIs url that your Server configures.
Create Service for File Upload
This service will use Axios to send HTTP requests.
There are 2 functions:
upload(file): POST form data with a callback for tracking upload progressgetFiles(): GET list of Files’ information
services/FileUploadService.js
import http from "../http-common";
const upload = (file, onUploadProgress) => {
let formData = new FormData();
formData.append("file", file);
return http.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
onUploadProgress,
});
};
const getFiles = () => {
return http.get("/files");
};
const FileUploadService = {
upload,
getFiles,
};
export default FileUploadService;
– First we import Axios as http from http-common.js.
– Inside upload() method, we use FormData to store key-value pairs. It helps to build an object which corresponds to HTML form using append() method.
– We pass onUploadProgress to exposes progress events. This progress event are expensive (change detection for each event), so you should only use when you want to monitor it.
– We call Axios post() to send an HTTP POST for uploading a File to Rest APIs Server and get() method for HTTP GET request to retrieve all stored files.
Create Functional Component for Upload Multiple Images
Let’s create a Multiple Image Upload UI with Progress Bar, Card, Button and Message.
First we create a React template with React Hooks (useState, useEffect) and import FileUploadService:
components/UploadImages.js
import React, { useState, useEffect, useRef } from "react";
import UploadService from "../services/FileUploadService";
const UploadImages = () => {
return (
);
};
export default UploadImages;
Then we define the state using React Hooks:
const UploadImages = () => {
const [selectedFiles, setSelectedFiles] = useState(undefined);
const [imagePreviews, setImagePreviews] = useState([]);
const [progressInfos, setProgressInfos] = useState({ val: [] });
const [message, setMessage] = useState([]);
const [imageInfos, setImageInfos] = useState([]);
const progressInfosRef = useRef(null);
...
}
We will use URL.createObjectURL() to get the image preview URL and put it into imagePreviews array. This method produces a DOMString including a URL describing the object provided in the parameter. The URL life is tied to the document in the window on which it was created.
Next we define selectFiles() function which helps us to get the selected Images from <input type="file" ...> element later.
const UploadImages = () => {
...
const selectFiles = (event) => {
let images = [];
for (let i = 0; i < event.target.files.length; i++) {
images.push(URL.createObjectURL(event.target.files[i]));
}
setSelectedFiles(event.target.files);
setImagePreviews(images);
setProgressInfos({ val: [] });
setMessage([]);
};
...
}
We use selectedFiles array for accessing current selected Files. Every file has a corresponding progress Info (percentage, file name) and index in progressInfos array.
const UploadImages = () => {
...
const uploadImages = () => {
const files = Array.from(selectedFiles);
let _progressInfos = files.map((file) => ({
percentage: 0,
fileName: file.name,
}));
progressInfosRef.current = {
val: _progressInfos,
};
const uploadPromises = files.map((file, i) => upload(i, file));
Promise.all(uploadPromises)
.then(() => UploadService.getFiles())
.then((files) => {
setImageInfos(files.data);
});
setMessage([]);
};
...
}
In the code above, for each image file, we invoke upload() function which call UploadService.upload() method with an index (i) parameter and return a Promise.
After all the processes are done, we call UploadService.getFiles() to get the images' information and assign the result to imageInfos state, which is an array of {name, url} objects.
So create following upload() function like this:
const UploadImages = () => {
...
const upload = (idx, file) => {
let _progressInfos = [...progressInfosRef.current.val];
return UploadService.upload(file, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
setProgressInfos({ val: _progressInfos });
})
.then(() => {
setMessage((prevMessage) => [
...prevMessage,
"Uploaded the image successfully: " + file.name,
]);
})
.catch(() => {
_progressInfos[idx].percentage = 0;
setProgressInfos({ val: _progressInfos });
setMessage((prevMessage) => [
...prevMessage,
"Could not upload the image: " + file.name,
]);
});
};
...
}
The progress will be calculated basing on event.loaded and event.total and percentage value is assigned to specific item of progressInfos by the index.
We also need to do this work in the Effect Hook useEffect() method which serves the same purpose as componentDidMount():
const UploadImages = () => {
...
useEffect(() => {
UploadService.getFiles().then((response) => {
setImageInfos(response.data);
});
}, []);
...
}
Now we return the Upload File UI. Add the following code inside return() block:
const UploadImages = () => {
...
return (
<div>
<div className="row">
<div className="col-8">
<label className="btn btn-default p-0">
<input
type="file"
multiple
accept="image/*"
onChange={selectFiles}
/>
</label>
</div>
<div className="col-4">
<button
className="btn btn-success btn-sm"
disabled={!selectedFiles}
onClick={uploadImages}
>
Upload
</button>
</div>
</div>
{progressInfos &&
progressInfos.val.length > 0 &&
progressInfos.val.map((progressInfo, index) => (
<div className="mb-2" key={index}>
<span>{progressInfo.fileName}</span>
<div className="progress">
<div
className="progress-bar progress-bar-info"
role="progressbar"
aria-valuenow={progressInfo.percentage}
aria-valuemin="0"
aria-valuemax="100"
style={{ width: progressInfo.percentage + "%" }}
>
{progressInfo.percentage}%
</div>
</div>
</div>
))}
{imagePreviews && (
<div>
{imagePreviews.map((img, i) => {
return (
<img className="preview" src={img} alt={"image-" + i} key={i} />
);
})}
</div>
)}
{message.length > 0 && (
<div className="alert alert-secondary mt-2" role="alert">
<ul>
{message.map((item, i) => {
return <li key={i}>{item}</li>;
})}
</ul>
</div>
)}
{imageInfos.length > 0 && (
<div className="card mt-3">
<div className="card-header">List of Images</div>
<ul className="list-group list-group-flush">
{imageInfos &&
imageInfos.map((img, index) => (
<li className="list-group-item" key={index}>
<p>
<a href={img.url}>{img.name}</a>
</p>
<img src={img.url} alt={img.name} height="80px" />
</li>
))}
</ul>
</div>
)}
</div>
);
}
In the code above, we use Bootstrap Progress Bar:
.progressas a wrapper- inner
.progress-barto indicate the progress .progress-barrequiresstyleto set the width by percentage.progress-baralso requiresroleand some aria attributes to make it accessible- label of the progress bar is the text within it
To display List of uploaded images, we iterate over imageInfos array using map() function. On each file item, we use img.url as href attribute and img.name for showing text.
The preview of uploading image is shown by iterating previewImages array.
Don't forget to export the function component:
const UploadImages = () => {
...
}
export default UploadImages;
You can simplify import statement with:
Absolute Import in React
Add Multiple Images Upload Functional Component to App Component
Open App.js, import and embed the ImagesUpload Component tag.
import React from "react";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import ImagesUpload from "./components/ImagesUpload";
function App() {
return (
<div className="container" style={{ width: "600px" }}>
<div className="my-3">
<h3>bezkoder.com</h3>
<h4>Multiple Images Upload in React.js</h4>
</div>
<ImagesUpload />
</div>
);
}
export default App;
Configure Port for React.js Multiple Image Upload App
Because most of HTTP Server use CORS configuration that accepts resource sharing restricted to some sites or ports, and if you use the Project for making Rest API server in:
- Node.js Express File Upload Rest API example
- Node.js Express File Upload to MongoDB example
- Node.js Express File Upload to Google Cloud Storage example
- Spring Boot Multipart File upload (to static folder) example
You need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
So our app will run at port 8081.
Run the App
Run this React Multiple Image Upload App with command: npm start.
Or: yarn start.
Open Browser with url http://localhost:8081/ and check the result.
Conclusion
Today we're learned how to build an React.js functional component for multiple Images upload using Hooks, Axios, Bootstrap with Progress Bars. We also provide the ability to show list of files, upload progress percentage, and to download file from the server.
You can find how to implement the Rest APIs Server at one of following posts:
- Node.js Express File Upload Rest API example
- Node.js Express File Upload to MongoDB example
- Node.js Express File Upload to Google Cloud Storage example
- Spring Boot Multipart File upload (to static folder) example
Happy Learning! See you again.
Source Code
You can find the complete source code for this tutorial at Github.
Typescript: React Typescript Multiple Image Upload example (with Preview)
Further Reading
- https://github.com/axios/axios
- React Hooks
- React Custom Hook tutorial with example
- Bootstrap 4 Progress
- React Hooks CRUD example with Axios and Web API
- React Hooks: JWT Authentication (without Redux) example
- React + Redux: JWT Authentication with Hooks
Fullstack:
- Spring Boot + React Redux example: Build a CRUD App
- React + Spring Boot + MySQL: CRUD example
- React + Spring Boot + PostgreSQL: CRUD example
- React + Spring Boot + MongoDB: CRUD example
- React + Node.js + Express + MySQL: CRUD example
- React Redux + Node.js + Express + MySQL: CRUD example
- React + Node.js + Express + PostgreSQL example
- React + Node.js + Express + MongoDB example
- React + Django + Rest Framework example
Serverless:
- React Hooks + Firebase Realtime Database: CRUD App
- React Hooks + Firestore example: CRUD app
