Use NProgress with Next.js (Router and fetch events)

vvo

Vincent Voyer

Posted on June 9, 2020

Use NProgress with Next.js (Router and fetch events)

Today I was trying to add NProgress https://github.com/rstacruz/nprogress to my Next.js project.

I wanted the progress bar to:

  1. show when switching routes / pages
  2. show when any fetch call is made
  3. display only after a delay, I don't want to show a loader at EVERY interaction, only when the requests are "slow"

Here's a demo of how NProgress looks like:
Demo of NProgress

Since I hit met some challenges while implementing all of that, I felt like it would be good to share how I did it. So here it is:

First, install the nprogress package:



npm install nprogress


Enter fullscreen mode Exit fullscreen mode

Then edit or create your _app.js and add:



// global styles are required to be added to `_app.js` per Next.js requirements.
import "nprogress/nprogress.css";

const TopProgressBar = dynamic(
  () => {
    return import("components/TopProgressBar");
  },
  { ssr: false },
);

export default function MyApp({ Component, pageProps }) {
  return <>
    <TopProgressBar />
    <Component {...pageProps} />
  </>
}


Enter fullscreen mode Exit fullscreen mode

Here we use dynamic imports and the ssr option to make sure our TopProgressBar is loaded only in browser environements.

If you're wondering how relatively loading components/TopProgressBar works, just configure you jsconfig.json as shown in the Next.js documentation.

Finally create components/TopProgressBar.js:



import Router from "next/router";
import NProgress from "nprogress";

let timer;
let state;
let activeRequests = 0;
const delay = 250;

function load() {
  if (state === "loading") {
    return;
  }

  state = "loading";

  timer = setTimeout(function () {
    NProgress.start();
  }, delay); // only show progress bar if it takes longer than the delay
}

function stop() {
  if (activeRequests > 0) {
    return;
  }

  state = "stop";

  clearTimeout(timer);
  NProgress.done();
}

Router.events.on("routeChangeStart", load);
Router.events.on("routeChangeComplete", stop);
Router.events.on("routeChangeError", stop);

const originalFetch = window.fetch;
window.fetch = async function (...args) {
  if (activeRequests === 0) {
    load();
  }

  activeRequests++;

  try {
    const response = await originalFetch(...args);
    return response;
  } catch (error) {
    return Promise.reject(error);
  } finally {
    activeRequests -= 1;
    if (activeRequests === 0) {
      stop();
    }
  }
};

export default function () {
  return null;
}


Enter fullscreen mode Exit fullscreen mode

Here we register to Next.js router events and monkey patch the global fetch. I was worried monkey patching fetch would fail to register early on but turns out it works 🤷‍♂️!

As you can see, TopProgressBar renders nothing, I guess there might be issues of doing things this way so if you encounter some, just let me know!

That's it!

If you're wondering if NProgress is still maintained because of the low number of commits and "high" number of issues, the good news is that they are working on a new version for 2020: https://github.com/rstacruz/nprogress/pull/218

Even if you're not using Next.js, you should be able to adapt this code to your favorite platform or framework.

💖 💪 🙅 🚩
vvo
Vincent Voyer

Posted on June 9, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related