The Fetch API does not provide a built-in download progress event like XMLHttpRequest. However, you can track download progress by reading the response as a ReadableStream and measuring the number of bytes received.
๐ What is Download Progress?
Download progress is the percentage of data that has been received from the server while a file or resource is being downloaded.
๐ก Why Track Download Progress?
- ๐ Display progress bars
- โฌ๏ธ Monitor large file downloads
- ๐ฌ Stream media content
- โก Improve user experience
- ๐ฑ Show loading percentages
๐ How It Works
- ๐ Send a Fetch request.
- ๐ฅ Read the response stream using response.body.getReader().
- ๐ฆ Receive data in chunks.
- ๐ Count the downloaded bytes.
- ๐ Calculate the download percentage.
๐ Basic Fetch Request
Simple Fetch
const response = await fetch("/file.zip");๐ Accessing the Response Stream
The response body is a ReadableStream. Use a reader to consume it chunk by chunk.
Get Stream Reader
const reader =
response.body.getReader();๐ Getting Total File Size
The server may send the total file size using theContent-Length response header.
Read Content-Length
const total =
Number(
response.headers.get("Content-Length")
);Note
โฌ๏ธ Reading Download Chunks
Read Stream
let received = 0;
while (true) {
const { done, value } =
await reader.read();
if (done) break;
received += value.length;
console.log(received);
}๐ Calculating Download Percentage
Progress Calculation
const percent =
(received / total) * 100;
console.log(
percent.toFixed(2) + "%"
);๐ Complete Example
Track Download Progress
async function downloadFile() {
const response =
await fetch("/large-file.zip");
const total =
Number(
response.headers.get(
"Content-Length"
)
);
const reader =
response.body.getReader();
let received = 0;
const chunks = [];
while (true) {
const { done, value } =
await reader.read();
if (done) break;
chunks.push(value);
received += value.length;
const percent =
(received / total) * 100;
console.log(
percent.toFixed(2) + "%"
);
}
const blob =
new Blob(chunks);
console.log(blob);
}
downloadFile();๐จ Updating a Progress Bar
HTML
<progress
id="progress"
value="0"
max="100">
</progress>Update Progress Bar
const progress =
document.getElementById("progress");
progress.value = percent;๐ Display Percentage
HTML
<p id="status">0%</p>Update Status
const status =
document.getElementById("status");
status.textContent =
percent.toFixed(1) + "%";๐ฅ Saving the Downloaded File
After all chunks are received, combine them into aBlob and create a temporary download link.
Download Blob
const blob =
new Blob(chunks);
const url =
URL.createObjectURL(blob);
const a =
document.createElement("a");
a.href = url;
a.download = "file.zip";
a.click();
URL.revokeObjectURL(url);๐ Important Objects
| Object | Purpose |
|---|---|
| response.body | Readable stream of the response. |
| getReader() | Reads stream data chunk by chunk. |
| read() | Reads the next available chunk. |
| Content-Length | Total response size (if provided). |
| Blob | Represents the completed downloaded file. |
โ ๏ธ Limitations
- ๐ Download percentage cannot be calculated if the server omits the Content-Length header.
- ๐ Some responses (such as compressed or chunked transfers) may not provide a usable total size.
- ๐พ Storing every chunk in memory can consume significant RAM for very large files.
- ๐ Stream support requires modern browsers.
๐ Fetch vs XMLHttpRequest
| Feature | Fetch API | XMLHttpRequest |
|---|---|---|
| Download Progress | โ Via streams | โ Built-in progress event |
| Promise-Based | โ Yes | โ No |
| Stream Support | โ Yes | โ ๏ธ Limited |
| Modern API | โ Yes | โ Legacy |
Note
โ Best Practices
- ๐ Show a progress bar for large downloads.
- โ ๏ธ Handle cases where Content-Length is unavailable.
- ๐งน Release object URLs using URL.revokeObjectURL() after use.
- ๐พ Avoid buffering extremely large files entirely in memory when possible.
- โ Handle network errors with try...catch.
๐ฏ Summary
The Fetch API can track download progress by reading the response as aReadableStream. By counting the bytes received and comparing them with the Content-Length header (when available), you can display real-time download percentages, update progress bars, and provide a smoother download experience for users.