Use NProgress with Next.js (Router and fetch events)
Tags: javascript, nprogress, nextjs, fetch
Published: 2020-06-09 14:19:11

---
title: Use NProgress with Next.js (Router and fetch events)
published: true
description: This article explains how to use NProgress to show a top progress bar whenever a fetch or Router event is ongoing in Next.js with React and JavaScript applications in general
tags: javascript,nprogress,nextjs,fetch
---

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](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/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](https://dev-to-uploads.s3.amazonaws.com/i/z27aof371eokm8awgest.gif)

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:

```bash
npm install nprogress
```

Then edit or create your [`_app.js`](https://nextjs.org/docs/advanced-features/custom-app) and add:

```js
// 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} />
  </>
}
```

Here we use [dynamic imports](https://nextjs.org/docs/advanced-features/dynamic-import) 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](https://nextjs.org/docs/advanced-features/module-path-aliases).

Finally create `components/TopProgressBar.js`:

```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;
}
```

Here we register to [Next.js router events](https://nextjs.org/docs/api-reference/next/router#routerevents) and [monkey patch](https://en.wikipedia.org/wiki/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.