A Simple Way to Build a Progress Bar for Your Website’s Image Uploader Using Filestack

If you’re building any kind of image uploader for a website, you’ll quickly realize that a basic file input isn’t enough. The moment a user tries to upload a large file, the experience breaks. They click “Upload,” the browser seems to hang, and they have no idea if it’s working or broken. This is the exact moment you realize you need a progress bar.
The thing is, building one from scratch is a classic developer trap. It seems straightforward, but you soon find yourself deep in the weeds of XMLHttpRequest
objects, event listeners, and DOM manipulation. It’s a ton of boilerplate for a feature that should just be standard.
Let’s walk through how to build one manually, and then I’ll show you why we ensured you would never have to.
Key Takeaways
- A progress bar is essential for a good user experience in any modern file uploader.
- Building a custom progress bar from scratch requires boilerplate JavaScript using
XMLHttpRequest
itsupload.onprogress
event. - The manual approach forces you to manage DOM updates and calculations yourself.
- A frontend-only progress bar is useless without a server-side endpoint to receive the file, adding more complexity.
- The Filestack picker automatically includes a reliable, professionally designed progress bar, with no extra code required.
The Manual Method With Vanilla JavaScript
If you were to build this feature yourself, you’d have to use the browser’s XMLHttpRequest
(XHR) API. It lets you make HTTP requests and, crucially for us, monitor the progress of an upload.
Here’s what that implementation looks like, piece by piece.
1. The HTML
First, you need a basic file input and a container for the progress bar.
Manual Progress Bar Demo
2. The CSS
Next, here are some simple styles to make our progress bar look like it’s actually progressing.
.progress-container {
width: 100%;
max-width: 400px;
background-color: #dfe1e6;
border-radius: 4px;
height: 24px;
margin-top: 1rem;
}
.progress-bar {
width: 0%;
height: 100%;
background-color: #0052cc;
text-align: center;
line-height: 24px;
color: white;
font-size: 14px;
border-radius: 4px;
transition: width 0.3s ease;
}
3. The JavaScript
This is where the tedious work comes in. We have to select the elements, listen for a file, create an XHR request, attach an event listener to the upload.onprogress
event, and manually update the DOM.
const fileInput = document.getElementById('fileInput');
const progressBar = document.getElementById('progressBar');
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) {
return;
}
// You still need to build a server endpoint to handle this request.
const url = 'https://your-backend-endpoint.com/upload';
const xhr = new XMLHttpRequest();
const formData = new FormData();
xhr.open('POST', url, true);
// The key event listener for tracking progress.
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
const percentComplete = Math.round((event.loaded / event.total) * 100);
progressBar.style.width = percentComplete + '%';
progressBar.textContent = percentComplete + '%';
}
};
xhr.onload = function() {
if (xhr.status === 200) {
progressBar.style.backgroundColor = '#07a85d'; // Green for success.
progressBar.textContent = 'Complete!';
} else {
progressBar.style.backgroundColor = '#de350b'; // Red for failure.
progressBar.textContent = 'Upload Failed!';
}
};
formData.append('uploadedFile', file);
xhr.send(formData);
});
This works, but look at the ceremony. You’re responsible for every step, and we haven’t even touched proper error handling, timeouts, or the fact that this code is useless without a corresponding backend to receive the file. This is exactly the kind of repetitive work we believe you shouldn’t be doing.
Note: This code shows the actual frontend logic for a manual upload. Because it requires a server to receive the file, running this HTML directly will cause a CORS policy error in your browser’s console. This error is a great example of the hidden backend complexity that this approach requires.
The Simple Way With Filestack
Now, let’s look at the alternative. When we designed the Filestack picker, we built all of that functionality directly into it. You don’t need to create an XMLHttpRequest
or write a progress handler. The progress bar is built-in and works automatically. It’s designed to give users clear feedback during and after the upload.
Here is the complete code to get a powerful image uploader for your website, complete with a progress bar, using Filestack.
Filestack Uploader with Automatic Progress Bar
Filestack Uploader Demo
Click the button and watch the built-in progress bar.
That’s it. That’s the entire implementation. You get a beautiful, full-featured uploader that shows detailed progress for each file, and you wrote about five lines of meaningful code to get it. We handle the rest.
Wrapping Up
The manual JavaScript approach gets the job done, but it’s a perfect illustration of a developer time sink. You’re forced to write boilerplate code to manage XHR events and manipulate the DOM, and that’s just for the frontend. You still have to build and maintain a server-side endpoint to actually receive the file. All this for a progress bar.
When we built the Filestack picker, we considered a progress bar to be table stakes, not an optional feature you should have to implement yourself. It’s a fundamental requirement for a good user experience. That’s why it’s included by default, along with robust error handling, multipart uploads for large files, and retry logic.
Your time is your most valuable resource. Spending it to reimplement solved problems is a losing game. The goal is to focus on your application’s core logic, not the plumbing. We handled the plumbing so you can get back to work on what matters.
This article was originally published on the Filestack blog.