Both nprogress and react-top-loading-bar are libraries designed to display a slim progress bar at the top of the screen during page loads or asynchronous operations. nprogress is a framework-agnostic vanilla JavaScript library that has been the industry standard for years, offering an imperative API to manually control progress states. react-top-loading-bar is a React-specific implementation that wraps this functionality into a component, aligning better with React's declarative paradigm and lifecycle management.
Both nprogress and react-top-loading-bar solve the same user experience problem: indicating that a background process is running by showing a slim bar at the top of the viewport. However, they approach this from different architectural angles. nprogress is a vanilla JavaScript utility that treats the progress bar as a global singleton, while react-top-loading-bar treats it as a React component. Let's compare how they handle integration, state, and customization.
nprogress operates as a global module.
// nprogress: Imperative global API
import NProgress from 'nprogress';
function startLoading() {
NProgress.start();
}
function finishLoading() {
NProgress.done();
}
react-top-loading-bar operates as a component.
// react-top-loading-bar: Component-based API
import ReactTopLoadingBar from 'react-top-loading-bar';
import { useRef } from 'react';
function App() {
const ref = useRef(null);
const startLoading = () => ref.current?.continuousStart();
const finishLoading = () => ref.current?.complete();
return (
<>
<ReactTopLoadingBar ref={ref} color="#29d" />
{/* Other components */}
</>
);
}
nprogress requires you to manage side effects manually.
NProgress.start() inside useEffect or event handlers.NProgress.done() is called even if errors occur.// nprogress: Manual lifecycle management
useEffect(() => {
NProgress.start();
fetchData().then(() => NProgress.done());
return () => NProgress.remove(); // Cleanup on unmount
}, []);
react-top-loading-bar aligns with React's lifecycle.
// react-top-loading-bar: Integrated lifecycle
useEffect(() => {
ref.current?.continuousStart();
fetchData().then(() => ref.current?.complete());
// No manual DOM cleanup needed
}, []);
nprogress uses CSS and a configuration object.
#nprogress.NProgress.configure({}).// nprogress: CSS + Config object
import 'nprogress/nprogress.css';
NProgress.configure({ showSpinner: false });
/* CSS */
#nprogress .bar { background: #29d; }
react-top-loading-bar uses props for styling.
// react-top-loading-bar: Props-based styling
<ReactTopLoadingBar
color="#29d"
height={3}
shadow
onRef={ref => setRef(ref)}
/>
nprogress works anywhere JavaScript runs.
// nprogress: Works in any JS context
// Can be used in a Vue mixin or Angular service identically
NProgress.start();
react-top-loading-bar is locked to the React ecosystem.
react and react-dom packages.// react-top-loading-bar: React only
// Will throw errors if used outside a React component
<ReactTopLoadingBar />
nprogress leaves error handling to the developer.
NProgress.done() in .finally() blocks.// nprogress: Manual error handling
fetchData()
.then(() => NProgress.done())
.catch(() => NProgress.done()); // Must remember this
react-top-loading-bar encourages structured handling.
// react-top-loading-bar: Safer ref calls
fetchData()
.then(() => ref.current?.complete())
.catch(() => ref.current?.complete());
Despite the architectural differences, both libraries share core functionality and goals. Here are key overlaps:
// Both achieve this visual result
// nprogress: <div id=
Choose nprogress if you are working in a vanilla JavaScript environment or need a single solution that works across multiple frameworks like Vue, Angular, and React without changing dependencies. It is ideal for legacy projects or architectures where global imperative control is preferred over component-based state management. However, be prepared to manually manage subscriptions and cleanup within React hooks to avoid memory leaks.
Choose react-top-loading-bar if you are building a modern React application and want a solution that integrates naturally with your component tree. It is suitable for projects that prioritize declarative patterns, as it handles mounting and unmounting automatically without requiring manual cleanup in useEffect. This package reduces boilerplate code and aligns with React best practices for UI elements.
Slim progress bars for Ajax'y applications. Inspired by Google, YouTube, and Medium.
Add nprogress.js and nprogress.css to your project.
<script src='nprogress.js'></script>
<link rel='stylesheet' href='nprogress.css'/>
NProgress is available via bower and npm and spm.
$ bower install --save nprogress
$ npm install --save nprogress
Simply call start() and done() to control the progress bar.
NProgress.start();
NProgress.done();
Using Turbolinks or similar? Ensure you're using Turbolinks 1.3.0+, and use this: (explained here)
$(document).on('page:fetch', function() { NProgress.start(); });
$(document).on('page:change', function() { NProgress.done(); });
$(document).on('page:restore', function() { NProgress.remove(); });
Add progress to your Ajax calls! Bind it to the jQuery ajaxStart and
ajaxStop events.
Make a fancy loading bar even without Turbolinks/Pjax! Bind it to
$(document).ready and $(window).load.
Percentages: To set a progress percentage, call .set(n), where n is a
number between 0..1.
NProgress.set(0.0); // Sorta same as .start()
NProgress.set(0.4);
NProgress.set(1.0); // Sorta same as .done()
Incrementing: To increment the progress bar, just use .inc(). This
increments it with a random amount. This will never get to 100%: use it for
every image load (or similar).
NProgress.inc();
If you want to increment by a specific value, you can pass that as a parameter:
NProgress.inc(0.2); // This will get the current status value and adds 0.2 until status is 0.994
Force-done: By passing true to done(), it will show the progress bar
even if it's not being shown. (The default behavior is that .done() will not
do anything if .start() isn't called)
NProgress.done(true);
Get the status value: To get the status value, use .status
minimumChanges the minimum percentage used upon starting. (default: 0.08)
NProgress.configure({ minimum: 0.1 });
templateYou can change the markup using template. To keep the progress
bar working, keep an element with role='bar' in there. See the default template
for reference.
NProgress.configure({
template: "<div class='....'>...</div>"
});
easing and speedAdjust animation settings using easing (a CSS easing string)
and speed (in ms). (default: ease and 200)
NProgress.configure({ easing: 'ease', speed: 500 });
trickleTurn off the automatic incrementing behavior by setting this to false. (default: true)
NProgress.configure({ trickle: false });
trickleRate and trickleSpeedYou can adjust the trickleRate (how much to increase per trickle) and trickleSpeed (how often to trickle, in ms).
NProgress.configure({ trickleRate: 0.02, trickleSpeed: 800 });
showSpinnerTurn off loading spinner by setting it to false. (default: true)
NProgress.configure({ showSpinner: false });
parentspecify this to change the parent container. (default: body)
NProgress.configure({ parent: '#container' });
Just edit nprogress.css to your liking. Tip: you probably only want to find
and replace occurrences of #29d.
The included CSS file is pretty minimal... in fact, feel free to scrap it and make your own!
Bugs and requests: submit them through the project's issues tracker.
Questions: ask them at StackOverflow with the tag nprogress.
Chat: join us at gitter.im.

NProgress © 2013-2014, Rico Sta. Cruz. Released under the MIT License.
Authored and maintained by Rico Sta. Cruz with help from contributors.
ricostacruz.com · GitHub @rstacruz · Twitter @rstacruz