Axios Upload Progress — Complete Guide with Examples
In this tutorial, you'll learn about Axios upload progress tracking. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Axios onUploadProgress callback lets you track file upload progress in real time, displaying percentage, speed, and estimated time remaining for large file uploads.
What You'll Learn
By the end of this tutorial, you'll track single and multiple file uploads, calculate upload speed and ETA, build a progress UI, handle upload cancellation, and display per-file progress for batch uploads.
Why It Matters
File uploads can take seconds to minutes. Without progress feedback, users think the page is frozen or the upload failed. A progress indicator builds confidence, and upload speed helps users choose between Wi-Fi and cellular connections.
Real-World Use
Durga Antivirus Pro's file submission portal uses onUploadProgress for users uploading suspicious files for analysis. The dashboard shows per-file progress, overall batch progress, and estimated analysis time after upload completes.
Where This Fits in Your Learning Path
flowchart LR
A["Download Progress"] --> B["**Upload Progress**"]
B --> C["Cookies & Auth"]
C --> D["HTTP/2 Adapters"]
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
Single File Upload with Progress
Track upload progress for a single file using FormData.
const fileInput = document.querySelector('#file-input')
const file = fileInput.files[0]
const formData = new FormData()
formData.append('file', file)
const response = await axios.post('/api/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: function(progressEvent) {
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
console.log(`Uploading: ${percent}%`)
}
})
console.log('Upload complete:', response.data)
Expected output: Console logs progress from 0% to 100% as the file uploads. On completion, the server response is logged.
Multiple File Upload with Per-File Progress
Track each file individually while uploading a batch.
const files = document.querySelector('#file-input').files
const uploads = Array.from(files).map((file, index) => {
const formData = new FormData()
formData.append('file', file)
return axios.post('/api/upload', formData, {
onUploadProgress: function(e) {
const percent = Math.round((e.loaded * 100) / e.total)
console.log(`File ${index + 1}/${files.length} (${file.name}): ${percent}%`)
}
})
})
const results = await Promise.all(uploads)
console.log('All uploads complete:', results.length)
Expected output: Each file uploads independently with its own progress log. The overall batch completes when all individual uploads finish.
Upload Dashboard with Speed and ETA
Build a full upload dashboard with metrics.
async function uploadFile(file) {
const formData = new FormData()
formData.append('file', file)
const startTime = Date.now()
const { data } = await axios.post('/api/upload', formData, {
onUploadProgress: function(e) {
const now = Date.now()
const elapsed = (now - startTime) / 1000
const percent = Math.round((e.loaded * 100) / e.total)
const speed = e.loaded / elapsed
const eta = (e.total - e.loaded) / speed
updateUI({
fileName: file.name,
percent,
loaded: e.loaded,
total: e.total,
speed,
eta
})
}
})
return data
}
function updateUI(info) {
document.querySelector('#file-name').textContent = info.fileName
document.querySelector('#progress-fill').style.width = info.percent + '%'
document.querySelector('#progress-text').textContent = info.percent + '%'
document.querySelector('#speed').textContent = formatSpeed(info.speed)
document.querySelector('#eta').textContent = formatTime(info.eta)
}
Expected output: The dashboard updates with file name, progress bar, percentage, upload speed, and ETA.
Upload with Cancellation
Combine onUploadProgress with AbortController for cancellable uploads.
let controller = null
async function startUpload(file) {
controller = new AbortController()
const formData = new FormData()
formData.append('file', file)
try {
const { data } = await axios.post('/api/upload', formData, {
signal: controller.signal,
onUploadProgress: function(e) {
const percent = Math.round((e.loaded * 100) / e.total)
console.log(`Upload: ${percent}%`)
document.querySelector('#cancel-btn').disabled = false
}
})
console.log('Upload complete:', data)
} catch (error) {
if (axios.isCancel(error)) {
console.log('Upload was canceled by user')
} else {
console.error('Upload failed:', error)
}
}
}
function cancelUpload() {
if (controller) {
controller.abort('User canceled upload')
document.querySelector('#cancel-btn').disabled = true
}
}
Expected output: Upload starts with progress. Clicking the cancel button aborts the request and shows a cancellation message.
Common Mistakes
1. Forgetting to set multipart/form-data header
Axios sets Content-Type automatically for FormData, including the boundary. Setting it manually may break the request.
2. Using onUploadProgress for download tracking
onUploadProgress tracks data sent to the server. Use onDownloadProgress for tracking received data.
3. Appending files incorrectly to FormData
Use formData.append('fieldName', file, filename) for proper multipart encoding. Omitting the filename may cause server Parsing issues.
4. Not handling file size validation before upload
Check file size on the client before uploading. A 2GB file will fail after minutes of uploading if the server rejects it.
5. Creating a new FormData for each progress update
FormData is created once before the request. Creating it inside onUploadProgress has no effect on the existing request.
Practice Questions
Which config option tracks upload progress? onUploadProgress callback in the request configuration.
What is the difference between onUploadProgress and onDownloadProgress? onUploadProgress tracks data sent to the server. onDownloadProgress tracks data received from the server.
How do you upload multiple files with individual progress? Create separate requests for each file, each with its own onUploadProgress callback.
Can you cancel an upload in progress? Yes. Pass an AbortController signal and call controller.abort() to cancel.
How does Axios determine upload total? From the Content-Length header of the request body. For FormData, Axios calculates it from the file sizes.
Challenge
Build a drag-and-drop file upload zone that accepts multiple files. Show a progress list with per-file progress bars, a total overall progress bar, and a cancel button for each file.
FAQ
Mini Project
Build a complete file uploader with drag-and-drop, multiple file support, per-file progress bars, overall progress, cancel per file, and upload speed display.
<div id="drop-zone" style="border:2px dashed #4ecdc4;padding:40px;text-align:center;border-radius:8px;cursor:pointer">
<p>Drop files here or click to select</p>
</div>
<div id="upload-list"></div>
<div id="overall-progress" style="display:none">
<div style="width:100%;background:#e0e0e0;border-radius:4px;overflow:hidden;margin-top:16px">
<div id="overall-fill" style="width:0%;height:20px;background:#4ecdc4;transition:width 0.3s"></div>
</div>
<p id="overall-text" style="font-size:12px;color:#666;margin-top:4px"></p>
</div>
<script>
const dropZone = document.querySelector('#drop-zone')
dropZone.addEventListener('click', () => {
const input = document.createElement('input')
input.type = 'file'
input.multiple = true
input.onchange = () => uploadFiles(input.files)
input.click()
})
dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.style.borderColor = '#ff6b6b' })
dropZone.addEventListener('dragleave', () => { dropZone.style.borderColor = '#4ecdc4' })
dropZone.addEventListener('drop', e => { e.preventDefault(); dropZone.style.borderColor = '#4ecdc4'; uploadFiles(e.dataTransfer.files) })
async function uploadFiles(files) {
const overallEl = document.querySelector('#overall-progress')
const overallFill = document.querySelector('#overall-fill')
const overallText = document.querySelector('#overall-text')
const listEl = document.querySelector('#upload-list')
overallEl.style.display = 'block'
const totalFiles = files.length
let completedFiles = 0
for (const file of files) {
const itemEl = document.createElement('div')
itemEl.className = 'upload-item'
itemEl.innerHTML = `<span>${file.name}</span><div style="width:100%;background:#eee;border-radius:4px;overflow:hidden"><div class="file-progress" style="width:0%;height:16px;background:#4ecdc4;transition:width 0.3s"></div></div><span class="file-status">0%</span>`
listEl.appendChild(itemEl)
const formData = new FormData()
formData.append('file', file)
const progressBar = itemEl.querySelector('.file-progress')
const statusEl = itemEl.querySelector('.file-status')
await axios.post('/api/upload', formData, {
onUploadProgress: function(e) {
const pct = Math.round((e.loaded * 100) / e.total)
progressBar.style.width = pct + '%'
statusEl.textContent = pct + '%'
}
})
completedFiles++
const overallPct = Math.round((completedFiles / totalFiles) * 100)
overallFill.style.width = overallPct + '%'
overallText.textContent = `${completedFiles} of ${totalFiles} files uploaded (${overallPct}%)`
statusEl.textContent = 'Done'
progressBar.style.background = '#2ecc71'
}
}
</script>
What's Next
Continue with authentication patterns:
| Tutorial | What You'll Learn |
|---|---|
| Cookies and Auth | Manage cookies, HTTP-only cookies, and auth tokens |
| HTTP/2 Adapters | Use HTTP/2 adapters for improved performance |
Related topics: FormData and file handling, multipart/form-data encoding.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro