Making a Progress Bar in Machine Coding Rounds

Progress bars are one of the most common UI elements used to indicate the progress of a task β whether itβs loading, uploading, or downloading. In frontend interviews and machine coding rounds, building a progress bar is a great way to showcase your understanding of React, state management, and user interaction.
Letβs walk through how to create a simple, smooth, and customizable progress bar in React.
( You can find the detailed code and working codesandbox example here ).
β¨ Building the Base Progress Bar
Weβll start with a basic version of the progress bar which increments automatically over time.
App.js
import { useEffect, useState } from "react";
import ProgressBar from "./components/ProgressBar";
import "./styles.css";
export default function App() {
const [value, setValue] = useState(0); // Tracks the current progress value
const [isCompleted, setIsCompleted] = useState(false); // Tracks whether the progress bar has completed
useEffect(() => {
// Set up an interval to increase the value every 100 milliseconds
const interval = setInterval(() => {
setValue((val) => val + 1);
}, 100);
// Clean up the interval when the component is unmounted
return () => clearInterval(interval);
}, []);
return (
<div className="App">
<h1 style={{ textAlign: "center" }}>Progress Bar</h1>
<ProgressBar
value={value}
onComplete={() => {
setIsCompleted(true); // Set completion state when value reaches 100
}}
/>
{isCompleted && <div>Completed</div>} {/* Conditionally render completion message */}
</div>
);
}
What This Code Does
We use
useState
to maintain the progress value and track completion.useEffect
is used to simulate an auto-incrementing timer.The
ProgressBar
component is passed the current value and a callback to be triggered on completion.
ProgressBar.js
import { useEffect, useState } from "react";
import "./ProgressBar.css";
const ProgressBar = ({ value, onComplete }) => {
const [percentage, setPercentage] = useState(value);
useEffect(() => {
const limitedValue = Math.min(100, Math.max(0, value)); // Clamp value between 0 and 100
setPercentage(limitedValue);
if (limitedValue >= 100) {
onComplete(); // Trigger callback if progress reaches 100
}
}, [value]);
return (
<div className="progress">
<span style={percentage > 49 ? { color: "white" } : { color: "black" }}>
{percentage.toFixed()}%
</span>
<div className="solidProgress" style={{ width: `${percentage}%` }} />
</div>
);
};
export default ProgressBar;
Explanation
We clamp the incoming
value
between 0 and 100 to keep the bar safe.useEffect
ensures the component updates smoothly asvalue
changes.When the value hits 100, we notify the parent component using
onComplete
.The percentage is visually displayed and styled based on its value.
ProgressBar.css
.progress {
width: 100%;
border-radius: 20px;
background: #e3e3e3;
overflow: hidden;
border: 1px solid black;
height: 20px;
position: relative;
}
.progress span {
position: absolute;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.solidProgress {
background-color: green;
height: 100%;
transition: width 0.2s ease;
}
Explanation
.progress
styles the container with a rounded background.The span element overlays text at the center.
.solidProgress
animates the growing green bar.
π Follow-Up Questions You Might Get (With Solutions)
1. Pause and Resume
Question: Can you pause and resume the progress?
Solution:
const [isPaused, setIsPaused] = useState(false);
const intervalRef = useRef(null);
useEffect(() => {
if (!isPaused && value < 100) {
intervalRef.current = setInterval(() => {
setValue((val) => val + 1);
}, 100);
}
return () => clearInterval(intervalRef.current);
}, [isPaused]);
<button onClick={() => setIsPaused(true)}>Pause</button>
<button onClick={() => setIsPaused(false)}>Resume</button>
2. Optimize Performance
Question: How would you avoid unnecessary renders?
Solution:
const ProgressBar = React.memo(({ value, onComplete }) => {
const percentage = Math.min(100, Math.max(0, value));
useEffect(() => {
if (percentage >= 100) onComplete();
}, [percentage]);
return (
<div className="progress">
<span>{percentage}%</span>
<div className="solidProgress" style={{ width: `${percentage}%` }} />
</div>
);
});
We use
React.memo
to prevent re-renders unless props change.State updates are limited only when value differs.
3. Add Smooth Transitions
Question: Can the animation be smoother?
Solution: Add a transition
to .solidProgress
:
.solidProgress {
transition: width 0.3s ease-in-out;
}
4. Circular Progress Bar
Question: How would you show progress as a circle?
Solution:
<svg width="120" height="120">
<circle r="50" cx="60" cy="60" stroke="lightgray" fill="none" strokeWidth="10" />
<circle
r="50" cx="60" cy="60"
stroke="green" fill="none" strokeWidth="10"
strokeDasharray={2 * Math.PI * 50}
strokeDashoffset={(1 - percentage / 100) * 2 * Math.PI * 50}
style={{ transition: "stroke-dashoffset 0.3s" }}
/>
</svg>
5. Add Tests
Question: How would you test the progress bar?
Solution:
A basic test would seem like :
it("should call onComplete when value is 100", () => {
const mock = jest.fn();
render(<ProgressBar value={100} onComplete={mock} />);
expect(mock).toHaveBeenCalled();
});
ποΈ File Upload + Progress Bar Example
Through the example below, we will observe how a progress bar can be synced with a file upload process.
We will be making a mock request to "https://httpbin.org/post".
export default function App() {
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const handleFileUpload = (event) => {
const file = event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append("file", file);
setUploading(true);
setProgress(0);
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://httpbin.org/post");
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
setProgress(percent);
}
};
xhr.onload = () => setUploading(false);
xhr.onerror = () => setUploading(false);
xhr.send(formData);
};
return (
<div className="App">
<h1>Upload a File</h1>
<input type="file" onChange={handleFileUpload} />
{uploading && <ProgressBar value={progress} />}
{!uploading && progress >= 100 && <div>Upload Completed!</div>}
</div>
);
}
Explanation
We capture a file using
<input type="file" />
Upload is done using
XMLHttpRequest
, which gives us progress events.The progress bar is updated in real-time as the file uploads.
π Conclusion
A progress bar may seem like a small UI element, but it opens up a world of things to test in machine coding rounds β state management, animation, cleanup, component design, performance, and real-world integrations like file uploads.
Practice building and extending it β maybe even try adding drag-and-drop upload or error handling next. Youβll not only ace this question but become better at thinking in reusable, modular UI patterns.
Happy coding! π - notyashgrover.in
Subscribe to my newsletter
Read articles from Yash Grover directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
