nprogress vs react-loader-spinner vs react-top-loading-bar
Top Loading Indicators for Web Applications
nprogressreact-loader-spinnerreact-top-loading-barSimilar Packages:

Top Loading Indicators for Web Applications

nprogress, react-loader-spinner, and react-top-loading-bar are all npm packages designed to provide visual feedback during asynchronous operations in web applications, but they serve different UI patterns and integration models. nprogress is a lightweight, framework-agnostic library that renders a slim progress bar at the top of the page, commonly used to indicate page navigation or data loading. react-loader-spinner offers a collection of animated spinner components built specifically for React, suitable for inline or modal loading states. react-top-loading-bar is a React-specific implementation of a top-of-page progress indicator, providing a declarative API and integration with React's component lifecycle.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
nprogress026,411-13511 years agoMIT
react-loader-spinner01,0351.62 MB104 months agoMIT
react-top-loading-bar075070.3 kB4a year agoMIT

Top Loading Indicators Compared: nprogress vs react-loader-spinner vs react-top-loading-bar

When users wait for content, a well-designed loading indicator reduces perceived latency and improves UX. The three libraries — nprogress, react-loader-spinner, and react-top-loading-bar — all address this need but with fundamentally different approaches: global top bar, inline spinners, and React-native top bar, respectively. Let’s compare them in real engineering contexts.

📏 Visual Pattern and Placement

nprogress renders a thin, colored bar fixed at the very top of the viewport (position: fixed; top: 0). It mimics the native browser loading bar seen in Chrome.

// nprogress: Imperative global control
import NProgress from 'nprogress';

NProgress.start();
fetch('/api/data').then(() => NProgress.done());

react-loader-spinner provides reusable React components that render inline wherever placed in the JSX tree — inside a button, a card, or a full-screen overlay.

// react-loader-spinner: Declarative inline spinner
import { Oval } from 'react-loader-spinner';

function LoadingCard({ isLoading }) {
  return (
    <div>
      {isLoading ? <Oval height={40} width={40} /> : <Content />}
    </div>
  );
}

react-top-loading-bar also renders a top-of-page bar like nprogress, but as a React component that lives in your app’s JSX hierarchy.

// react-top-loading-bar: Component-based top bar
import TopLoadingBar 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 (
    <>
      <TopLoadingBar color="#29D" ref={ref} />
      <MainApp startLoading={startLoading} finishLoading={finishLoading} />
    </>
  );
}

⚙️ Integration Model: Imperative vs Declarative

nprogress is imperative and global. You call NProgress.start() and NProgress.done() directly. This works outside React but creates tight coupling to side effects.

// Common in route guards
router.beforeEach((to, from, next) => {
  NProgress.start();
  next();
});

router.afterEach(() => NProgress.done());

react-loader-spinner is purely declarative and local. You conditionally render the spinner based on React state. No global state or side effects.

function SubmitButton({ isSubmitting }) {
  return (
    <button disabled={isSubmitting}>
      {isSubmitting ? <TailSpin height="20" width="20" /> : 'Submit'}
    </button>
  );
}

react-top-loading-bar blends both: it’s declarative in structure (you render the component once) but imperative in control (you call methods via a ref). This gives you the visual style of nprogress with React lifecycle safety.

// Safe for SSR — no direct DOM access in render
useEffect(() => {
  loadingRef.current?.continuousStart();
  fetchData().finally(() => loadingRef.current?.complete());
}, []);

🧪 Server-Side Rendering (SSR) and Testing

nprogress manipulates the DOM directly on import. This breaks in SSR environments like Next.js unless you guard usage with typeof window !== 'undefined'.

// Risky in SSR
if (typeof window !== 'undefined') {
  NProgress.start();
}

react-loader-spinner is fully compatible with SSR because it renders standard React elements. No DOM access during render.

// Safe everywhere
<Oval ariaLabel="loading-indicator" />

react-top-loading-bar avoids DOM access during initial render and uses refs for runtime control, making it SSR-safe by design.

// Renders empty div on server, activates on client
<TopLoadingBar color="#f00" ref={ref} />

🎨 Customization and Animation Control

nprogress allows limited customization via CSS overrides or a config object:

NProgress.configure({
  minimum: 0.1,
  showSpinner: false,
  speed: 500
});

But you can’t change the core animation logic without forking.

react-loader-spinner offers multiple built-in spinner types (Oval, Circles, TailSpin, etc.), each accepting props like height, width, color, and ariaLabel. You can even pass custom SVG paths.

<Puff
  visible={true}
  height="80"
  width="80"
  color="#4fa94d"
  ariaLabel="puff-loading"
/>

react-top-loading-bar supports color, height, and shadow via props, plus programmatic control over progress simulation (continuousStart, staticStart, complete).

<TopLoadingBar
  color="#29D"
  height={3}
  shadow={true}
  ref={ref}
/>

🔄 Real-World Usage Scenarios

Scenario 1: SPA Route Transitions

You’re using React Router and want a top bar during page loads.

  • Best: react-top-loading-bar — integrates cleanly with React lifecycle and router hooks.
  • ⚠️ Alternative: nprogress — works but requires useEffect guards and isn’t React-idiomatic.
  • Not suitable: react-loader-spinner — wrong visual pattern (inline, not top bar).

Scenario 2: Form Submission with Inline Feedback

A user clicks “Save” and you show a spinner inside the button.

  • Best: react-loader-spinner — designed exactly for this.
  • Not suitable: Both top-bar libraries — they occupy global screen space unnecessarily.

Scenario 3: Dashboard with Multiple Async Widgets

Each widget loads independently and shows its own loading state.

  • Best: react-loader-spinner — per-widget spinners avoid visual noise from a single top bar.
  • ⚠️ Possible but awkward: Top-bar libraries would require complex coordination to avoid flickering.

📌 Summary Table

Featurenprogressreact-loader-spinnerreact-top-loading-bar
Visual StyleTop barInline spinnersTop bar
FrameworkVanilla JSReact-onlyReact-only
Control ModelImperative (global)Declarative (local)Declarative + Ref imperative
SSR Safe❌ (requires guards)
Custom AnimationsLimited (CSS only)✅ (multiple built-in types)Limited (progress simulation)
Best ForGlobal nav loadingLocalized loading statesReact apps needing top bar

💡 Final Recommendation

  • Need a top bar and aren’t using React? → nprogress.
  • Building a React app and want a top bar? → react-top-loading-bar.
  • Need inline, contextual spinners? → react-loader-spinner.

Don’t mix top bars and inline spinners arbitrarily — pick one primary loading pattern per app to maintain visual consistency. And always ensure loading states are accessible (e.g., with aria-busy or aria-live regions), regardless of which library you choose.

How to Choose: nprogress vs react-loader-spinner vs react-top-loading-bar

  • nprogress:

    Choose nprogress if you need a minimal, framework-agnostic top-loading bar that works across any JavaScript environment—not just React—and you prefer manual imperative control over its visibility. It’s ideal for simulating native browser-like loading behavior during route transitions or API calls without JSX overhead. However, it requires direct DOM manipulation and doesn’t integrate with React’s state model, which can complicate testing or SSR.

  • react-loader-spinner:

    Choose react-loader-spinner when you need flexible, visually distinct inline loading indicators (like rotating circles, bars, or custom SVG animations) that fit within your UI layout rather than occupying the top edge of the viewport. It’s perfect for localized loading states—such as inside buttons, cards, or modals—and offers multiple animation types out of the box. Since it’s purely presentational and stateless, you’ll manage visibility via React state, making it predictable and testable.

  • react-top-loading-bar:

    Choose react-top-loading-bar if you’re building a React application and want a top-of-page progress bar with a declarative, component-based API that integrates cleanly with React’s rendering cycle. It supports programmatic control via refs and automatically handles mounting/unmounting, making it safer for SSR and easier to coordinate with React Router or data fetching hooks. It’s the best choice when you want the visual pattern of nprogress but with idiomatic React ergonomics.

README for nprogress

NProgress

Slim progress bars for Ajax'y applications. Inspired by Google, YouTube, and Medium.

Installation

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

Basic usage

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(); });

Ideas

  • 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.

Advanced usage

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

Configuration

minimum

Changes the minimum percentage used upon starting. (default: 0.08)

NProgress.configure({ minimum: 0.1 });

template

You 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 speed

Adjust animation settings using easing (a CSS easing string) and speed (in ms). (default: ease and 200)

NProgress.configure({ easing: 'ease', speed: 500 });

trickle

Turn off the automatic incrementing behavior by setting this to false. (default: true)

NProgress.configure({ trickle: false });

trickleRate and trickleSpeed

You 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 });

showSpinner

Turn off loading spinner by setting it to false. (default: true)

NProgress.configure({ showSpinner: false });

parent

specify this to change the parent container. (default: body)

NProgress.configure({ parent: '#container' });

Customization

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!

Resources

Support

Bugs and requests: submit them through the project's issues tracker.
Issues

Questions: ask them at StackOverflow with the tag nprogress.
StackOverflow

Chat: join us at gitter.im.
![Chat](http://img.shields.io/badge/gitter-rstacruz / nprogress-brightgreen.svg)

Thanks

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

Status npm version spm package