Skip to content

Axios Progress Events — Complete Guide with Examples

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you'll learn about Axios download progress tracking. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Axios onDownloadProgress callback lets you track download progress in real time, displaying percentage, speed, estimated time remaining, and building custom progress indicators for large file downloads.

What You'll Learn

By the end of this tutorial, you'll use onDownloadProgress to track download completion, calculate download speed and ETA, build progress bars, and handle streams with progress.

Why It Matters

Users abandon downloads that show no progress. A progress bar builds trust and sets expectations. Tracking download speed helps diagnose slow connections and gives users actionable feedback.

Real-World Use

DodaZIP shows a detailed download progress bar when downloading update packages. It displays percentage, MB/s speed, time remaining, and total size. This transparency keeps users informed during large downloads.

Where This Fits in Your Learning Path

flowchart LR
    A["Cancellation"] --> B["**Progress Events**"]
    B --> C["Upload Progress"]
    C --> D["Cookies & Auth"]
    D --> E["Axios Advanced"]
    style B fill:#f97316,stroke:#c2410c,color:#fff
    style A fill:#e5e7eb,stroke:#9ca3af,color:#374151
    style E fill:#22c55e,stroke:#16a34a,color:#fff

Basic Download Progress

The onDownloadProgress callback receives a ProgressEvent with loaded and total properties.

const response = await axios.get('/api/large-file', {
  responseType: 'blob',
  onDownloadProgress: function(progressEvent) {
    const percentCompleted = Math.round(
      (progressEvent.loaded * 100) / progressEvent.total
    )
    console.log(`Downloaded: ${percentCompleted}%`)
  }
})

Expected output: The console logs progress from 0% to 100% as the file downloads.

Progress Bar UI

Build a visual progress bar that updates in real time.

const progressBar = document.querySelector('#progress-bar')
const progressText = document.querySelector('#progress-text')

await axios.get('/api/large-file', {
  responseType: 'blob',
  onDownloadProgress: function(e) {
    const percent = Math.round((e.loaded * 100) / e.total)
    progressBar.style.width = percent + '%'
    progressBar.textContent = percent + '%'
    progressText.textContent = `Downloaded ${formatBytes(e.loaded)} of ${formatBytes(e.total)}`
  }
})

function formatBytes(bytes) {
  if (bytes < 1024) return bytes + ' B'
  if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'
  return (bytes / 1048576).toFixed(1) + ' MB'
}
<div style="width:100%;background:#e0e0e0;border-radius:4px;overflow:hidden">
  <div id="progress-bar" style="width:0%;height:24px;background:#4ecdc4;text-align:center;color:white;line-height:24px;font-size:12px">0%</div>
</div>
<p id="progress-text" style="font-size:14px;color:#666">Starting download...</p>

Expected output: A progress bar fills from 0% to 100% with a formatted byte display beneath it.

Download Speed and ETA

Calculate transfer speed and estimated time remaining.

let startTime = Date.now()
let lastLoaded = 0

const response = await axios.get('/api/large-file', {
  responseType: 'blob',
  onDownloadProgress: function(e) {
    const now = Date.now()
    const elapsed = (now - startTime) / 1000
    const speed = e.loaded / elapsed  // bytes per second
    const remaining = (e.total - e.loaded) / speed
    const percent = Math.round((e.loaded * 100) / e.total)

    console.log(`${percent}% | ${formatSpeed(speed)} | ETA: ${formatTime(remaining)}`)
    lastLoaded = e.loaded
  }
})

function formatSpeed(bytesPerSec) {
  if (bytesPerSec < 1024) return bytesPerSec.toFixed(0) + ' B/s'
  if (bytesPerSec < 1048576) return (bytesPerSec / 1024).toFixed(1) + ' KB/s'
  return (bytesPerSec / 1048576).toFixed(1) + ' MB/s'
}

function formatTime(seconds) {
  if (seconds < 60) return Math.round(seconds) + 's'
  return Math.round(seconds / 60) + 'm ' + Math.round(seconds % 60) + 's'
}

Expected output: Console shows a live update of percentage, download speed, and estimated time remaining.

Downloading with Response Type Blob

When downloading binary data, set responseType to blob and get a downloadable URL.

const { data } = await axios.get('/api/report.pdf', {
  responseType: 'blob',
  onDownloadProgress: function(e) {
    console.log(`Downloading report: ${Math.round((e.loaded * 100) / e.total)}%`)
  }
})

// Create a download link
const url = URL.createObjectURL(data)
const link = document.createElement('a')
link.href = url
link.download = 'report.pdf'
link.click()
URL.revokeObjectURL(url)

Expected output: The file downloads with a progress log, then triggers a browser download of the PDF.

Common Mistakes

1. Forgetting responseType: 'blob' for binary downloads

Without blob responseType, Axios tries to parse binary data as JSON, causing corruption and incorrect total size detection.

2. Assuming progressEvent.total is always available

total is null for responses without Content-Length header. Handle the case where total is undefined.

3. Not throttling progress updates in the UI

onDownloadProgress fires frequently. Throttle UI updates to every 100ms to avoid layout thrashing.

4. Using progress events in Node.js without proper streams

Node.js progress events work differently. Use the onDownloadProgress in Axios or manually track progress with streams.

5. Calculating ETA incorrectly early in the download

Early ETA estimates fluctuate wildly. Wait for at least 10% progress before displaying ETA.

Practice Questions

  1. Which config option enables download progress tracking? onDownloadProgress callback in the request config.

  2. What properties does the progress event contain? loaded (bytes completed) and total (total bytes). Total may be null without Content-Length.

  3. How do you calculate download speed? Divide loaded bytes by elapsed time in seconds since the download started.

  4. Why use responseType: 'blob' for downloads? It ensures binary data is returned as a Blob instead of being parsed as text/JSON.

  5. Can you track progress for requests without Content-Length? Partially. loaded updates but total is null. Show indeterminate progress or downloaded bytes only.

Challenge

Build a download manager that tracks multiple simultaneous downloads, each with its own progress bar, speed display, and ETA. Auto-retry failed downloads up to 3 times.

FAQ

Does onDownloadProgress work with all HTTP methods?

Yes. Progress tracking works with GET, POST, PUT, and any method that downloads a response body.

Can I track progress for multiple downloads separately?

Yes. Each request has its own onDownloadProgress callback. Use closures to associate progress with specific download elements.

Is download progress accurate?

Accuracy depends on the server sending Content-Length. Without it, only loaded bytes are available without total.

Does Axios support onDownloadProgress in Node.js?

Yes. Axios supports progress events in Node.js. The event object has the same loaded/total interface.

How often does onDownloadProgress fire?

It fires whenever the underlying transport reports progress, typically on every received chunk. Frequency depends on chunk size and network speed.


Mini Project

Build a complete download dashboard. Enter a URL, click download, and see a real-time progress bar with speed, ETA, percentage, and total size. When complete, show a success animation and offer to open the file.

async function startDownload(url) {
  const startTime = Date.now()
  const statusEl = document.querySelector('#status')
  const progressEl = document.querySelector('#progress-fill')
  const detailsEl = document.querySelector('#details')

  try {
    const response = await axios.get(url, {
      responseType: 'blob',
      onDownloadProgress: function(e) {
        const now = Date.now()
        const elapsed = (now - startTime) / 1000
        const percent = e.total ? Math.round((e.loaded * 100) / e.total) : 0
        const speed = e.loaded / elapsed
        const eta = e.total ? ((e.total - e.loaded) / speed) : 0

        progressEl.style.width = percent + '%'
        statusEl.textContent = `${percent}%`
        detailsEl.textContent = `Downloaded: ${formatBytes(e.loaded)}${e.total ? ' of ' + formatBytes(e.total) : ''} | Speed: ${formatSpeed(speed)} | ETA: ${formatTime(eta)}`
      }
    })

    progressEl.style.width = '100%'
    statusEl.textContent = 'Complete!'
    detailsEl.textContent = `File size: ${formatBytes(response.data.size)}`

    // Trigger download
    const blobUrl = URL.createObjectURL(response.data)
    const a = document.createElement('a')
    a.href = blobUrl
    a.download = url.split('/').pop()
    a.click()
    URL.revokeObjectURL(blobUrl)

  } catch (error) {
    statusEl.textContent = 'Failed'
    detailsEl.textContent = error.message
  }
}

What's Next

Learn about upload progress tracking:

Tutorial What You'll Learn
Upload Progress Track file upload progress for form data
Cookies and Auth Manage cookies and authentication with Axios

Related topics: Blob and File objects, URL.createObjectURL.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro