Nextjs: show loading progress on page navigation

The problem #

Nextjs renders the initial page on the server side and the subsequent page on the client. Because of that while navigating using Next Link the browser does not show loading progress. This leads to giving the user the perception that nothing happened or the page is broken.

The solution #

To fix that a custom progress bar can be shown.

1. Install nprogress #

npm i -S nprogress
if using typescript also do npm i -D @types/nprogress

2. Change _app.js file to call nprogress on router events #
// _app.jsx or _app.tsx

// add import
import nProgress from "nprogress";
import { Router } from "next/router";
import "nprogress/nprogress.css""routeChangeStart", nProgress.start);"routeChangeError", nProgress.done);"routeChangeComplete", nProgress.done);
// end

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
export default MyApp;
3. (Optional) Customize progress bar #

By default nprogress show a blue progress bar on top of page. That may not match the Website look and feel. To customize

/* Make clicks pass-through */
#nprogress {
    --primary-color: #ff0000;
    pointer-events: none;

  #nprogress .bar {
    background: var(--primary-color);
    position: fixed;
    z-index: 1031;
    top: 0;
    left: 0;
    width: 100%;
    height: 2px;

  /* Fancy blur effect */
  #nprogress .peg {
    display: block;
    position: absolute;
    right: 0px;
    width: 100px;
    height: 100%;
    box-shadow: 0 0 10px var(--primary-color), 0 0 5px var(--primary-color);
    opacity: 1.0;

    -webkit-transform: rotate(3deg) translate(0px, -4px);
        -ms-transform: rotate(3deg) translate(0px, -4px);
            transform: rotate(3deg) translate(0px, -4px);

  /* Remove these to get rid of the spinner */
  #nprogress .spinner {
    display: block;
    position: fixed;
    z-index: 1031;
    top: 15px;
    right: 15px;

  #nprogress .spinner-icon {
    width: 18px;
    height: 18px;
    box-sizing: border-box;
    border: solid 2px transparent;
    border-top-color: var(--primary-color);
    border-left-color: var(--primary-color);
    border-radius: 50%;

    -webkit-animation: nprogress-spinner 400ms linear infinite;
            animation: nprogress-spinner 400ms linear infinite;

  .nprogress-custom-parent {
    overflow: hidden;
    position: relative;

  .nprogress-custom-parent #nprogress .spinner,
  .nprogress-custom-parent #nprogress .bar {
    position: absolute;

  @-webkit-keyframes nprogress-spinner {
    0%   { -webkit-transform: rotate(0deg); }
    100% { -webkit-transform: rotate(360deg); }
  @keyframes nprogress-spinner {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }

Since you've made it this far, sharing this article on your favorite social media network would be highly appreciated ! For feedback, please ping me on Twitter.