Bind Progress Bars to Axios Lifecycle Events
Integrating visual progress indicators like NProgress with Axios improves user experience by delivering immediate feedback during asynchronous HTTP operations. This article details the primary techniques for binding progress bars to Axios lifecycle events, covering global interceptors, concurrent request management via active request counting, and handling granular download/upload progress events.
Global Request and Response Interceptors
The most common technique for binding NProgress to Axios is using Axios interceptors. Interceptors hook directly into the lifecycle of HTTP requests right before they are dispatched and immediately after a response or error is received.
- Request Interceptor: Triggers
NProgress.start()when an outgoing request is initiated. - Response Interceptor: Triggers
NProgress.done()when the server responds successfully or when an error occurs.
import axios from 'axios';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
// Configure NProgress options if needed
NProgress.configure({ showSpinner: false });
// Bind to request lifecycle
axios.interceptors.request.use(
(config) => {
NProgress.start();
return config;
},
(error) => {
NProgress.done();
return Promise.reject(error);
}
);
// Bind to response lifecycle
axios.interceptors.response.use(
(response) => {
NProgress.done();
return response;
},
(error) => {
NProgress.done();
return Promise.reject(error);
}
);Managing Concurrent Requests with Request Counters
When multiple API requests occur simultaneously, the standard
interceptor approach causes the progress bar to terminate prematurely as
soon as the first request finishes. To resolve this, use an
active request counter to ensure NProgress.done() is only
called when all pending network calls have settled.
import axios from 'axios';
import NProgress from 'nprogress';
let activeRequests = 0;
const calculateProgress = () => {
if (activeRequests === 0) {
NProgress.done();
}
};
axios.interceptors.request.use(
(config) => {
if (activeRequests === 0) {
NProgress.start();
}
activeRequests++;
return config;
},
(error) => {
activeRequests--;
calculateProgress();
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
activeRequests--;
calculateProgress();
return response;
},
(error) => {
activeRequests--;
calculateProgress();
return Promise.reject(error);
}
);Granular Progress with Upload and Download Hooks
For large payloads or file transfers, indeterminate progress bars do
not reflect actual data transfer states. Axios provides
onUploadProgress and onDownloadProgress
configuration options that expose the native browser progress
events.
You can bind these events to NProgress.set(percentage)
to reflect real-time byte completion:
import axios from 'axios';
import NProgress from 'nprogress';
export const uploadFile = (file) => {
const formData = new FormData();
formData.append('file', file);
NProgress.start();
return axios.post('/api/upload', formData, {
onUploadProgress: (progressEvent) => {
if (progressEvent.total) {
const percentCompleted = progressEvent.loaded / progressEvent.total;
NProgress.set(percentCompleted);
}
},
}).finally(() => {
NProgress.done();
});
};Scoped Axios Instances
To prevent internal or polling requests from triggering the progress
bar across the entire application, bind NProgress strictly to dedicated
Axios instances rather than the global axios object.
import axios from 'axios';
import NProgress from 'nprogress';
// Create an isolated instance for user-driven UI actions
const apiClient = axios.create({
baseURL: 'https://api.example.com',
});
apiClient.interceptors.request.use((config) => {
NProgress.start();
return config;
});
apiClient.interceptors.response.use(
(response) => {
NProgress.done();
return response;
},
(error) => {
NProgress.done();
return Promise.reject(error);
}
);
export default apiClient;By leveraging scoped instances, request counters, and transfer progress hooks, NProgress integrates cleanly with Axios while preventing visual glitches during complex network interactions.