How to Export CSV and PDF Files Using Axios
Handling file downloads such as CSVs and PDFs in modern web applications requires configuring the Axios HTTP client to process raw binary streams rather than parsing standard JSON payloads. This guide provides a straightforward, robust pattern to request binary data from an API, extract filenames from response headers, trigger browser-based file downloads via Blobs, and gracefully handle server-side errors returned as binary objects.
1. Set the
responseType to 'blob'
By default, Axios attempts to parse server responses as JSON. When
downloading files (PDF, CSV, Excel, ZIP), you must explicitly set
responseType: 'blob' in the request configuration. This
ensures Axios returns the raw binary data encapsulated within a
Blob object.
import axios from 'axios';
const fetchFile = async (url) => {
return await axios.get(url, {
responseType: 'blob', // Critical for binary data handling
});
};2. Implement the Download Trigger
Browsers do not automatically prompt a download when receiving a file
via an AJAX request. You must programmatically create a temporary anchor
(<a>) tag, assign it an object URL generated from the
Blob, trigger a click event, and clean up the object URL to prevent
memory leaks.
const triggerBrowserDownload = (blob, defaultFilename = 'download') => {
// Create a temporary URL pointing to the Blob
const blobUrl = window.URL.createObjectURL(blob);
// Create an invisible <a> element
const link = document.createElement('a');
link.href = blobUrl;
link.setAttribute('download', defaultFilename);
document.body.appendChild(link);
// Trigger download and cleanup
link.click();
link.parentNode.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
};3. Extracting the Filename from Headers
If your backend specifies the filename using the
Content-Disposition header, extract it directly from the
Axios response headers. Note that your server must expose this header
via CORS using
Access-Control-Expose-Headers: Content-Disposition.
const extractFilename = (response, fallbackName) => {
const disposition = response.headers['content-disposition'];
if (disposition && disposition.includes('filename=')) {
const matches = disposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (matches && matches[1]) {
return matches[1].replace(/['"]/g, '');
}
}
return fallbackName;
};4. Handling Error Responses Correctly
When responseType: 'blob' is set, error payloads (like a
400 or 500 JSON response) are also returned as Blobs. To display server
error messages, convert the error Blob back to JSON text:
const parseBlobError = async (error) => {
if (error.response && error.response.data instanceof Blob) {
try {
const errorText = await error.response.data.text();
return JSON.parse(errorText);
} catch {
return { message: 'An unknown error occurred while downloading the file.' };
}
}
return error;
};5. Complete Reusable Export Utility
Combine the steps into a single, clean function for exporting any file type:
import axios from 'axios';
/**
* Downloads a file from an API endpoint cleanly using Axios.
* @param {string} url - The endpoint URL.
* @param {string} defaultFilename - Fallback filename with extension (e.g., 'report.pdf', 'data.csv').
* @param {object} params - Optional query parameters.
*/
export const exportFile = async (url, defaultFilename = 'export.bin', params = {}) => {
try {
const response = await axios.get(url, {
params,
responseType: 'blob',
});
const filename = extractFilename(response, defaultFilename);
triggerBrowserDownload(response.data, filename);
} catch (error) {
const parsedError = await parseBlobError(error);
console.error('File export failed:', parsedError);
throw parsedError;
}
};Usage Example
// Export a PDF report
await exportFile('/api/reports/quarterly', 'quarterly-report.pdf');
// Export a CSV dataset with filters
await exportFile('/api/users/export', 'users.csv', { status: 'active' });