How to Make a Loading Progress Bar on Next.js 13 App Router

Published by felbenini
29/7/2023 - 20:41
How to Make a Loading Progress Bar on Next.js 13 App Router
By following this guide, you'll successfully integrate a loading progress bar into your Next.js 13 App Router application. This enhancement not only improves the user experience but also ensures seamless route transitions, even with the changes brought by the new App Router structure.

Discover how to implement a loading progress bar using Next.js 13 App Router. The latest version of Next.js brings exciting tools and fresh ways of accomplishing tasks. However, the transition from pages router to app router requires rethinking how to integrate old functionalities, such as libraries that have long been part of the framework.

One such case involves the next-js-progressbar library, a popular tool that utilizes NProgress to display a loading progress bar during route transitions. Although this library relies on the router events of Next.js to monitor route changes, it currently lacks compatibility with the newest Nest.js App Router structure. As a result, the library doesn't seamlessly function as it did with the old pages router.

But fear not! A workaround exists to integrate this library into the new App Router using event listeners. Keep reading to explore the step-by-step guide below.

Creating the Loading Bar Component

This tutorial assumes you've already initialized your Next.js project using create-next-app. Let's dive right in. Before proceeding, you need to install the next-js-progressbar library within your project. Use npm to achieve this by executing the following command:

npm i nextjs-progressbar

Once that's done, proceed to create a client component that leverages nextjs-progressbar. This component will be incorporated into your layout.tsx file.

progressbar.tsx
'use client'
import NextNProgress from 'nextjs-progressbar';

export default function LoadingBar() {
    return (
        <NextNProgress />
    )
}
layout.tsx
//...
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={montserrat.className}>
        <LoadingBar />
        {children}
      </body>
    </html>
  )
}

However, upon changing routes, you'll notice that the progress bar doesn't trigger. To address this issue, we need to detect when routes change. The most effective approach is to utilize the useEffect hook and event listeners. By doing so, clicking a link or anchor element triggers the progress bar.

progressbar.tsx
'use client'
import NextNProgress from 'nextjs-progressbar';
import NProgress from "nprogress";
import { useEffect } from 'react';

type PushStateInput = [
    data: any,
    unused: string,
    url?: string | URL | null | undefined
];  

export default function LoadingBar() {
    useEffect(() => {
        NProgress.configure({ showSpinner: false });

        const handleAnchorClick = (event: MouseEvent) => {
            const targetUrl = new URL((event.currentTarget as HTMLAnchorElement).href);
            const targetPage = (event.currentTarget as HTMLAnchorElement).target
            const currentUrl = new URL(location.href);
            if (targetUrl !== currentUrl && targetUrl.hostname === currentUrl.hostname && targetUrl.pathname !== currentUrl.pathname && targetPage !== 'blank') {
              NProgress.start();
            }
          };
      
          const handleMutation: MutationCallback = () => {
            const anchorElements = document.querySelectorAll("a");
            anchorElements.forEach((anchor) =>
              anchor.addEventListener("click", handleAnchorClick)
            );
          };
      
          const mutationObserver = new MutationObserver(handleMutation);
          mutationObserver.observe(document, { childList: true, subtree: true });
      
          window.history.pushState = new Proxy(window.history.pushState, {
            apply: (target, thisArg, argArray: PushStateInput) => {
              NProgress.done();
              return target.apply(thisArg, argArray);
            },
          });
    })
    return (
        <NextNProgress />
    )
}

In the above implementation:

  1. Clicking an anchor element triggers an event.
  2. This event starts NProgress, initiating the loading bar.
  3. If the anchor element's URL hostname differs from the current URL, NProgress won't trigger.
  4. Similarly, if the anchor element's pathname matches the current pathname, NProgress won't trigger.
  5. Finally, NProgress won't trigger if the anchor element's target is set to 'blank'.

That's a wrap! Hopefully, this guide proves helpful to you. By the way, the code presented here is also employed for the progress bar on the website you're currently visiting.