โฌ‡๏ธ Fetch Download Progress in JavaScript โ€“ Complete Tutorial

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.

>>"Using streams, Fetch allows you to process data chunk by chunk as it arrives."

๐Ÿ’ก Why Track Download Progress?

  • ๐Ÿ“Š Display progress bars
  • โฌ‡๏ธ Monitor large file downloads
  • ๐ŸŽฌ Stream media content
  • โšก Improve user experience
  • ๐Ÿ“ฑ Show loading percentages

๐Ÿ›  How It Works

  1. ๐ŸŒ Send a Fetch request.
  2. ๐Ÿ“ฅ Read the response stream using response.body.getReader().
  3. ๐Ÿ“ฆ Receive data in chunks.
  4. ๐Ÿ“Š Count the downloaded bytes.
  5. ๐Ÿ“ˆ 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

If the server does not send theContent-Length header, you can still count downloaded bytes, but you cannot calculate an accurate percentage.

โฌ‡๏ธ 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

ObjectPurpose
response.bodyReadable stream of the response.
getReader()Reads stream data chunk by chunk.
read()Reads the next available chunk.
Content-LengthTotal response size (if provided).
BlobRepresents 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

FeatureFetch APIXMLHttpRequest
Download Progressโœ… Via streamsโœ… Built-in progress event
Promise-Basedโœ… YesโŒ No
Stream Supportโœ… Yesโš ๏ธ Limited
Modern APIโœ… YesโŒ Legacy

Note

Fetch does not emit a built-in download progress event. Progress tracking is achieved by manually reading the response stream withReadableStream.

โœ… 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.