Why My Upload Progress Bar Jumped Straight to 100%
A fast image upload was skipping every step and flashing straight from 0% to 100%. Here's what actually caused it — and the fix that made the progress bar smooth again
Muhammad Ali

While working on an AI platform, I hit a small bug that turned out to be more interesting than it looked. The image upload worked perfectly — files reached the backend, URLs came back, everything was fine. But the progress bar was broken: instead of climbing smoothly, it jumped straight from 0% to 100%.
0
100
No 5%, no 40%, no 75% — just zero, then done. On a slow upload you'd expect a smooth climb. Here, the bar basically flashed and disappeared.
### The setup
The upload used Azure Blob Storage's uploadData, with an onProgress callback updating a React state value:
const [progress, setProgress] = useState(0)
await blockBlobClient.uploadData(file, {
concurrency: 1,
onProgress: (progressEvent) => {
const newProgress = Math.floor(
(progressEvent.loadedBytes / file.size) * 100
)
setProgress(newProgress)
},
})
Looks correct. So why the jump?
The real cause
There were actually two things working together.
1. The upload was too fast
The images were small and the connection was quick, so the whole upload finished almost instantly. Azure's onProgress only fired once or twice — often just at the start and the end. There simply weren't any 40% or 75% events to show, because by the time the callback ran, the file was already up.
On top of that, React batches state updates. Even when a couple of progress events did fire close together, React collapsed them into a single render — so the intermediate values never made it to the screen.
2. The finally block reset progress instantly
This was the part that actually hid the 100%:
try {
await blockBlobClient.uploadData(file, { /* ... onProgress ... */ })
// ...
} finally {
setIsUploading(false)
setProgress(0) // ← runs the moment the upload finishes
}
The finally block runs immediately after the upload resolves. So even in the cases where progress did reach 100, this line reset it back to 0 in the very next render. The user never saw the full bar — it was wiped before it could paint.
So the bug was a combination: a fast upload that skipped the middle steps, and a reset that erased the final state before it showed.
The fix
The goal was simple: let the bar actually reach and show 100%, and don't reset it the instant the upload finishes.
1. Set progress to 100 explicitly on success — don't rely on onProgress to land exactly on 100:
try {
const uploadResponse = await blockBlobClient.uploadData(file, {
concurrency: 1,
onProgress: (progressEvent) => {
const newProgress = Math.floor(
(progressEvent.loadedBytes / file.size) * 100
)
setProgress(newProgress)
},
})
setProgress(100) // guarantee the bar reaches full
setError('')
// ... return url
}
**2. Don't reset in finally **
move the reset out, and give the 100% a moment to be visible before clearing:
finally {
setIsUploading(false)
// reset AFTER a short delay, so 100% actually shows
setTimeout(() => setProgress(0), 600)
}
That short delay is the key. It lets the completed bar sit at 100% long enough for the user to see it, then quietly resets for the next upload.
The lesson
The bug wasn't really about upload speed — it was about UI state living long enough to be seen. When an operation finishes faster than the UI can render its steps, two things help:
- Explicitly set the final state (100) instead of hoping progress events land there.
- Don't tear down that state immediately — a completed state that's reset in the same tick never gets painted.
A fast upload is a good problem to have. But if you're showing progress, the UI needs a beat to actually show the finish — otherwise all your users see is a flash.
Written by
Muhammad Ali
