React
Uploads with real progress: what to measure
Upload progress comes from the bytes the browser reports, not from a timer: onUploadProgress measures the send, not the work.
Key points
- Real progress comes from the XMLHttpRequest upload event, not from a timer.
- fetch does not expose send progress: use XMLHttpRequest or axios.
- 100% of the send does not mean the work is done: show a second phase.
Upload progress comes from the bytes the browser says it has sent, through the upload.onprogress event on XMLHttpRequest: a timer that ticks measures nothing.
Why fetch does not expose it
With fetch the request body is an opaque stream and no send-progress event exists: you can read the download in chunks, not the upload. Use XMLHttpRequest or a library that wraps it, such as axios, released under the MIT licence.
The code that measures
function carica(file, onProgress) {
const body = new FormData();
body.append("file", file);
return axios.post("/api/upload", body, {
onUploadProgress: (e) => onProgress(Math.round((e.loaded / e.total) * 100)),
});
}
The percentage is loaded divided by total: it measures the bytes sent, not the processing of the file on the server.
The traps
- 100% is not done: the send is complete, and the response may arrive much later.
- total at zero: with
Transfer-Encoding: chunkedthe total is missing and the bar stays indeterminate. - Several files: add the bytes of every file, never the percentages.
- Cancelling:
AbortControllerstops the request and the event stops arriving.
Installation
npm i axios
The install adds a single MIT dependency; onUploadProgress works in browsers that expose XMLHttpRequest, which leaves out tests on jsdom.
Live records in the catalogue
Three file upload components: file-upload-card, file-upload-2 and avatar-uploader. To browse by category start from 111,985 components.
The limitation
The bar measures the send, not the outcome: when the server queues the file, the reader sees 100% and a wait. Honest progress needs a second phase, with a job id to poll.
Where to continue
The route handler for files is in uploads with Next.js; the comparison of the two field schemes is in controlled and uncontrolled forms; the framework choice is in Vue 3 vs React.
Frequently asked questions
Why does the bar reach 100% and never finish?
Because it measures the send, not the processing. Show a separate state while you wait for the response.
Does fetch support upload progress?
No, not for the request body. With fetch you read the download in chunks, not the bytes going out.
How do I test the bar?
Fire a progress event on a fake XMLHttpRequest: jsdom sends no bytes and emits no real events.
Do I need axios?
No. XMLHttpRequest is enough; axios wraps the same API and adds an MIT dependency.
Is the remaining time reliable?
No. The bytes are real data, while the estimate depends on a speed that keeps changing.