
This blog covers how to implement the functionality of uploading multiple files in a web application using React, Node.js, and TypeScript with a file upload component. You’ll build a fully functional React file uploader with a progress bar that tracks real-time upload progress. We’ll also integrate a simple Node.js backend using Multer to handle server-side uploads efficiently. Whether you’re building a React multiple file upload feature or enhancing user experience with a file upload progress bar, this guide walks through each step in detail. If you’re working with a reactjs development company, this setup can easily be adapted into a larger production-grade application.
Prerequisites
1. Basic understanding of Node.js, React, TypeScript, and npm.
2. Node.js installed on your system (if you don’t have it, download it from here).
Setting Up a React Project
First, create a new React TS with Vite project:
npm create vite@latest new-project-name --template react
Code language: JavaScript (javascript)
Follow the CLI prompts to complete the setup. This approach is especially helpful when you’re aiming to hire reactjs developers who can efficiently implement scalable frontend architectures using TypeScript and modern tooling.
Step 1: Install Required Dependencies
1. Navigate to the project directory.
cd new-project-name
Code language: JavaScript (javascript)
2. Install Dependencies
npm install
3. Run the Project
npm run dev
Note: If you get a Sass-embedded error, then run this
npm install -D sass-embedded
Step 2: Basic Example of Uploading Multiple Files

1. Create a file-upload.tsx file inside the src folder.
src/file-upload.tsx
import React, { useRef, useState } from "react";
import "./file-upload.scss";
function FileUpload() {
const [files, setFiles] = useState<File[]>([]);
const [progress, setProgress] = useState<number>(0);
const [uploading, setUploading] = useState(false);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [filesDetails, setFilesDetails] = useState<
{ fileName: string; dataURL: string }[]
>([]);
const [userName, setUserName] = useState("");
const inputRef = useRef<HTMLInputElement>(null);
const callGetFilesApi = async (username: string) => {
try {
const response = await fetch(
`http://localhost:8080/api/files/upload/${username}`
);
if (response.ok) {
const data = await response.json();
setFilesDetails(data);
} else {
throw new Error("Failed to fetch images.");
}
} catch {
setErrorMessage("Failed to fetch images.");
}
};
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
setFiles(Array.from(e.target.files));
}
};
const downloadImage = (dataUrl: string, fileName: string) => {
const link = document.createElement("a");
link.href = dataUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
const handleFileUpload = async () => {
const formData = new FormData();
formData.append("username", userName.trim());
files.forEach((file) => formData.append("files", file));
setUploading(true);
setProgress(0);
setSuccessMessage(null);
setErrorMessage(null);
try {
await uploadFiles(formData, (percent) => {
const displayedProgress = Math.min(percent, 98);
setProgress(displayedProgress);
});
await new Promise((res) => setTimeout(res, 300));
setProgress(100);
await new Promise((res) => setTimeout(res, 500));
// Show success message
setSuccessMessage("Files uploaded successfully!");
setTimeout(() => setSuccessMessage(null), 5000);
} catch {
setErrorMessage("Error uploading files. Please try again.");
setTimeout(() => setErrorMessage(null), 5000);
} finally {
setUploading(false);
}
};
const uploadFiles = (
formData: FormData,
onProgress: (percent: number) => void
): Promise<void> => {
return new Promise<void>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
onProgress(percent);
}
};
xhr.onload = () => {
if (xhr.status === 200) {
const responseJson = JSON.parse(xhr.responseText);
const user = responseJson.data[0];
inputRef.current && (inputRef.current.value = "");
setFiles([]);
setUserName("");
callGetFilesApi(user);
resolve();
} else {
reject("Upload failed");
}
};
xhr.onerror = () => reject("Upload failed");
xhr.open("POST", "http://localhost:8080/api/files/upload", true);
xhr.send(formData);
});
};
return (
<div className="center-div">
<div className="file-upload-container" style={{ width: "300px" }}>
<h1 className="file-upload-heading">Upload Files</h1>
<input
type="text"
className="user-input"
name="userName"
placeholder="User Name"
value={userName}
onChange={(e) => setUserName(e.target.value)}
/>
<input
type="file"
multiple
ref={inputRef}
onChange={handleFileChange}
className="file-upload-input"
/>
<button
onClick={handleFileUpload}
className="file-upload-button"
disabled={!userName || files.length === 0}
>
{uploading ? "Uploading..." : "Upload"}
</button>
{uploading && (
<>
<div className="progress-container">
<div
className="progress-bar"
style={{ width: '${progress}%' }}
></div>
</div>
<p className="progress-text">{Math.round(progress)}%</p>
</>
)}
{successMessage && <p className="success-message">{successMessage}</p>}
{errorMessage && <p className="error-message">{errorMessage}</p>}
</div>
{filesDetails.length > 0 && (
<div className="div" style={{ width: "60%" }}>
<div className="img-div" style={{ padding: "10px" }}>
<div className="images">
{filesDetails.map((file, index) => {
const extension =
file.fileName.split(".").pop()?.toLowerCase() || "";
const isImage = ["jpg", "jpeg", "png", "gif", "webp"].includes(
extension
);
const getFileIcon = (ext: string): string => {
const icons: { [key: string]: string } = {
pdf: "",
doc: "",
docx: "",
xls: "",
csv: "",
xlsx: "",
txt: "",
zip: "",
rar: "",
};
return icons[ext] || "";
};
return (
<div key={index} className="img-container">
{isImage ? (
<img
src={file.dataURL}
className="image"
height={50}
width={50}
alt={file.fileName}
/>
) : (
<div
className="file-icon"
style={{
height: "100px",
width: "100px",
fontSize: "60px",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: "#eee",
borderRadius: 4,
}}
>
{getFileIcon(extension)}
</div>
)}
<button
className="download-button"
onClick={() => downloadImage(file.dataURL, file.fileName)}
>
Download
</button>
</div>
);
})}
</div>
</div>
</div>
)}
</div>
);
}
export default FileUpload;
Code language: PHP (php)
If you’re exploring practical uses of TypeScript in React, check out this detailed guide on Learn TypeScript with React by Building a CRUD Application.
Explanation:
1. State Variables:
- files: Stores selected files for the upload using the file upload react component.
- Progress: Tracks the upload progress bar as a percentage (0–100).
- uploading: Boolean flag indicating if the file upload in React JS is in progress.
- successMessage: Stores the success message to be displayed after a successful upload.
- errorMessage: Stores the error message to be displayed if the upload fails.
- filesDetails: Holds details of previously uploaded files (file name and data URL for images).
- userName: Stores the username entered by the user.
- inputRef: A reference to the file input element used to reset the input field after upload.
2. Fetching Previously Uploaded Files:
- callGetFilesApi: Fetches the list of uploaded files from the server using the provided username. Updates the files state with the fetched data or shows an error if the request fails.
3. Handling File Selection:
- handleFileChange: Triggered when the user selects files. It updates the files state with the selected files.
4. File Upload Process:
- handleFileUpload: Handles the file upload process.
- Creates a FormData object and appends the selected files and username.
- Calls the uploadFiles function to upload the files.
- Updates the progress state during the upload to show progress in percentage.
- Displays a success message on successful upload and an error message if it fails.
5. Uploading Files with Progress:
- uploadFiles: Uses XMLHttpRequest to upload the files to the server.
- Tracks upload progress using the onprogress event and calls onProgress to update the progress.
- Upon success, it clears the file input, resets the username, and fetches the list of uploaded files.
- On failure, it rejects with an error message.
6. Rendering the Upload UI:
- Displays:
- A text input for the username.
- A file input for file selection.
- A button to trigger the upload, which shows “Uploading…” when in progress.
- Success/Error messages based on the result of the upload.
- A progress bar that shows the upload progress.
- If files have been previously uploaded, they show them with a download button next to each file.
- All file icons are visible except images.
7. Downloading Files:
- downloadImage: A function that allows users to download the uploaded images. Triggers a download by simulating a click event.
Key Concepts:
- React Hooks: Used for state management (useState) and managing references (useRef).
- File Upload: Uses FormData to package files and XMLHttpRequest to handle the upload and progress.
- Progress Bar: Updates as files are uploaded, providing visual feedback to the user.
- Error and Success Handling: Displays messages based on the success or failure of the upload.
- Downloading: Users can download images they’ve uploaded.
For more tutorials that reinforce concepts like this, refer to our Top 10 List of Best React Tutorials: Learn React JS for Free in 2022.

2. Create a file-upload.scss file inside the src folder.
src/file-upload.scss
Please copy the below scss codes
/.file-upload-container {
display: flex;
flex-direction: column;
padding: 20px;
font-family: Arial, sans-serif;
}
.file-upload-heading {
font-size: 2rem;
margin-bottom: 20px;
color: #333;
text-align: center;
}
.file-upload-input {
padding: 10px;
font-size: 1rem;
border-radius: 5px;
border: 1px solid #ccc;
margin-bottom: 20px;
cursor: pointer;
}
.user-input {
padding: 10px;
font-size: 1rem;
margin-bottom: 20px;
border-radius: 5px;
border: 1px solid #ccc;
width: 276px;
cursor: pointer;
}
.file-upload-button {
padding: 10px 20px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.file-upload-button:hover {
background-color: #45a049;
}
.file-upload-button:disabled {
background-color: #ccc; /* Light grey */
color: #888; /* Dark grey text */
cursor: not-allowed;
}
.center-div {
display: flex;
flex-direction: column;
align-items: center !important;
width: 100% !important;
min-height: 85vh !important;
}
.file-list-container {
width: 100%;
}
.file-item {
margin-bottom: 20px;
text-align: center;
width: 100%;
}
.file-name {
margin-bottom: 5px;
font-size: 1.1rem;
color: #333;
}
.progress-container {
width: 100%;
background-color: #f3f3f3;
border-radius: 5px;
margin-top: 10px;
}
.progress-bar {
height: 10px;
border-radius: 5px;
background-color: #4caf50;
}
.progress-text {
font-size: 1rem;
color: #555;
}
.images {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.img-container {
position: relative;
display: inline-block;
}
.image {
width: 100px;
height: 100px;
border-radius: 8px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
}
.download-button {
position: absolute;
bottom: 2px;
right: 0px;
background: green;
color: white;
border: none;
border-radius: 8px;
padding: 0px;
cursor: pointer;
transition: background 0.3s;
height: 22px;
width: 100px;
text-align: center;
}
.img-style {
height: 22px !important;
width: 20px !important;
}
.img-div {
justify-content: center;
align-items: center;
background-color: #f8f9fa;
padding: 10px !important;
}
Code language: PHP (php)
3. Setup of App.tsx
Replace below code with src/App.tsx codes and import FileUpload.
import "./App.css";
import FileUpload from "./file-upload";
function App() {
return <FileUpload />;
}
export default App;
Code language: JavaScript (javascript)
Explanation:
It is the root component of the React app, containing the main UI structure and application logic. All other components are nested within App.tsx to build the application’s layout and functionality. For the file react file upload component, you need to create code file-upload.tsx and scss file
In app.css just update root id css
#root {
text-align: center;
width: 100%;
}
Code language: CSS (css)
4. Run the Project
npm run dev
Note: If you get a Sass-embedded error, then run this
npm install -D sass-embedded
Setting up the Node.js Project

Let’s start by setting up the project environment using Node.js, Express and Multer.
These steps are commonly followed by teams at any professional nodeJS development company building robust server-side APIs for file handling.
Step 1: Initialize the Project
- Create a new directory for your project:
mkdir node-your-project
- Move into the project directory:
cd node-your-project
- Initialize the node project:
npm init -y
Step 2: Install Dependencies
- Install the core dependencies for the Express framework
npm install express
- Install the typescript
npm install typescript
- TypeScript Configuration
npx tsc --init
- Install Multer
npm install multer
- Install cors
npm install cors
- Install TypeScript and necessary type definitions
npm install --save-dev typescript ts-node @types/express @types/multer @types/cors
Code language: CSS (css)
Basic Example: Upload Multiple Files
Step 1: Set Up Basic Express Server
1. Create src folder and create a app.ts file
Create the src/app.ts
file and set up a basic file upload server using Express.
src/app.ts
import express from "express";
import cors from "cors";
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
export default app;
Code language: JavaScript (javascript)
Explanation:
This code sets up an Express server with CORS support, JSON and URL-encoded data parsing, and routes file-related requests to a file-routes module. The server is then exported for use in other parts of the application.
2. Create a server.ts file
Create the src/server.ts and start the Express server on port 8080 and log a message to confirm it’s running.
src/server.ts
import app from "./app";
const PORT = 8080;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Code language: JavaScript (javascript)
Explanation:
Please import the first app.ts and This code starts the Express server and makes it accessible on the specified port.
3. Run the Project :
npx ts-node src/server.ts
Step 2: Create Files Upload Post API
1. Create controller file in src/controller folder
src/controller/files-upload-controller.ts
import { NextFunction, Request, Response } from "express";
export const uploadFiles = async (req: Request, res: Response) => {
try {
const files = req.files as Express.Multer.File[];
const uploadResult = await uploadFileService(files, req.body.username);
res.status(200).json({
message: "Files uploaded successfully",
data: uploadResult,
});
} catch (error) {
res.status(500).json({
message: "File upload failed",
});
}
};
Code language: JavaScript (javascript)
Explanation:
This controller handles file uploads, uses a service to process the files, and sends a success or failure response back to the client. And you will get import error of service file , Then you need to create service file and required function and then import in controller
2. Create service file in src/service folder
src/service/file-services.ts
import path from "path";
import fs from "fs";
export const uploadFileService = (
files: Express.Multer.File[],
username: string
) => {
return new Promise<string[]>((resolve, reject) => {
try {
const uploadDir = path.join(__dirname, `../../uploads/${username}`);
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
const filePaths = files.map((file) => {
const filePath = path.join(uploadDir, file.originalname);
fs.writeFileSync(filePath, file.buffer);
return username;
});
resolve(filePaths);
} catch (error) {
reject(error);
}
});
};
Code language: JavaScript (javascript)
Explanation:
This service function saves uploaded files to a user-specific directory using multer and returns the paths where the files were saved.
3. Create routes file in src folder
src/file-routes.ts
import express from "express";
import multer from "multer";
import {
uploadFiles,
} from "./controller/files-upload-controller";
const upload = multer({ storage: multer.memoryStorage() });
const router = express.Router();
router.post("/upload", upload.array("files"), uploadFiles);
export default router;
Code language: JavaScript (javascript)
Explanation:
First import the uploadFiles controller function. and this route handles file uploads to a specific folder. It uses the files-upload-controller for handling the logic and multer middleware for managing the file storage.
API URL- POST- http://localhost:8080/api/files/upload
Request – formData
username: user
files: (binary)
files: (binary)
4. Add these route codes in app.ts file and please import fileRoutes
import fileRoutes from "./file-routes"
app.use("/api/files", fileRoutes);
Code language: JavaScript (javascript)
5. Run the project
npx ts-node src/server.ts
Step 3: Get Uploaded Files along with these API’s codes

1. Create a getUploadedFiles function inside
src/controller/files-upload-controller.ts
export const getUploadedFiles = async (
req: Request,
res: Response,
next: NextFunction
) => {
try {
const images = await getUploadedFilesByUser(req.params.username);
res.status(200).send(images);
} catch (error) {
return next(error);
}
};
Code language: JavaScript (javascript)
Explanation:
It’s causing service function errors, so you need to create a function within the service file. After creating the function, import it into the controller. This function will handle retrieving the images uploaded by the user and send them back in the response, while also managing any errors that may arise during the process.
2. Create a getUploadedFilesByUser function inside
src/service/file-services.ts
export const getUploadedFilesByUser = (username: string) => {
const uploadDir = path.join(__dirname, `../../uploads/${username}`);
return getImageDataURLs(uploadDir);
};
const getImageDataURLs = async (uploadedDir: string) => {
const files = fs.readdirSync(uploadedDir);
const imageDataURLs: { fileName: string; dataURL: string }[] = [];
files.forEach((file) => {
const filePath = path.join(uploadedDir, file);
const fileType = path.extname(file).substring(1);
const imageBuffer = fs.readFileSync(filePath);
const base64Data = imageBuffer.toString("base64");
// Create Data URL
const dataURL = `data:image/${fileType};base64,${base64Data}`;
imageDataURLs.push({ fileName: file, dataURL });
});
return imageDataURLs;
};
Code language: JavaScript (javascript)
Explanation:
This code provides functionality to retrieve user-uploaded images as Data URLs.
- getUploadedFilesByUser(username: string):
- Builds the path to the user’s upload folder.
- Call getImageDataURLs(uploadDir) to fetch image data in that folder.
- getImageDataURLs(uploadedDir: string):
- Reads the image files in the given directory.
- Converts each image to a base64-encoded Data URL.
- Returns an array of objects containing each image’s file name and its Data
- URL.
3. Add a route for get uploaded files inside src/file-routes.ts
Please add below code for get uploaded files
import {
getUploadedFiles
} from "./controller/files-upload-controller";
router.get("/upload/:username", getUploadedFiles);
Code language: JavaScript (javascript)
This routes /upload/:username to get uploaded user’s files from folder.
API URL- GET- http://localhost:8080/api/files/upload/:username
4. Run the project
npx ts-node src/server.ts
Conclusion
In this tutorial, we’ve learned how to upload multiple files on a website using a React file uploader and display a file upload progress bar to track the upload. We explored how to send files from the frontend (React) to the backend (Node.js) and update the upload progress bar in real-time as files are uploaded.
For instance, in an app where users upload photos, they can select multiple images, and as each image uploads, a progress bar will show how much of the file has been uploaded.
If you are considering Node.js for your backend and need guidance on choosing the right NodeJS development company, you can refer to our detailed guide on How to Select the Right NodeJS Development Company?.
With React, you handle the file selection and display the progress bar, while on the Node.js side, tools like Multer manage the actual file uploads. This approach makes the file upload in React JS smoother and provides users with immediate feedback on their uploads.You can find the full source code for this React TypeScript file upload project with backend integration on GitHub. Feel free to explore, clone, and adapt it to your needs.

Author's Bio:

Sachin Kumar is a software engineer at Mobisoft Infotech with over five years of hands-on experience in developing scalable and high-performance applications. He combines deep technical expertise with a passion for solving real-world problems through clean, efficient code.