Skip to main content

One post tagged with "Frontend Development"

View all tags

Next.js Route Navigation Progress Bar: Enhancing User Experience with BProgress

· 5 min read
Kaypen
Frontend Developer

This tutorial is also available as a video on YouTube 👉 【Next.js】Route Navigation Progress Bar

Hello, I'm Kaypen.

Let's start with a bad example.

In Dify.ai, when you click to navigate to another page, there's a waiting period before the page actually transitions.

Dify.ai lacks progress feedback during page navigation

However, during this waiting time, I don't know whether the navigation was successful or not, so I end up clicking multiple times until the page finally changes.

This is a poor user experience 👎

The solution is simple. Let's look at how GitHub handles navigation interactions.

GitHub's progress bar effect during page navigation

As you can see, GitHub displays a progress bar during navigation, clearly telling users - "I'm navigating, please wait."

So how can we implement this effect in Next.js?

We can achieve this using the BProgress library.

BProgress official website homepage

BProgress is a lightweight progress bar component library that supports Next.js 15+, as well as other frameworks like Remix and Vue.

For using BProgress, I've created a demo project nextjs-progress-bar-demo. Let's clone this project first:

git clone git@github.com:wukaipeng-dev/nextjs-progress-bar-demo.git

Then enter the project directory:

cd nextjs-progress-bar-demo

First, install the dependencies:

npm install @bprogress/next

Start the project:

npm run dev

Next.js progress bar demo project interface

As you can see, this is a simple Next.js project with three pages: Home, Login, and Register.

The main branch already has the progress bar configured. Let's switch to the without-progress-bar-demo branch:

git checkout without-progress-bar-demo

In this branch, we haven't configured the progress bar, so no progress bar will be displayed during page navigation.

Next, let's import ProgressProvider in the root layout app/layout.tsx:

'use client';

import "./globals.css";
import { ProgressProvider } from '@bprogress/next/app';

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<ProgressProvider
height="4px"
color="#4c3aed"
options={{ showSpinner: false }}
shallowRouting
>
{children}
</ProgressProvider>
</body>
</html>
);
}

Now, we can see that when navigating between the home page and login page, or between login and register pages, a progress bar will be displayed.

Page navigation effect after integrating BProgress

The ProgressProvider parameters are:

  • height: The height of the progress bar
  • color: The color of the progress bar
  • options: Progress bar configuration. Here showSpinner is set to false, meaning no animated loading icon will be displayed.
  • shallowRouting: Whether to enable shallow routing. If enabled, when only the route's query parameters change (e.g., ?page=1 to ?page=2), the progress bar won't reload.

However, after successful login, clicking to navigate won't show the progress bar.

Progress bar not showing when using router.push

This is because navigation between the home page and login page, or between login and register pages, uses the <Link> component.

The <Link> component actually renders as an <a> tag, and BProgress adds click events to all <a> components to show the progress bar.

We can check in DevTools → Elements → <a> → Event Listeners whether a click event has been added:

Viewing Link component's click event listeners in DevTools

But after successful login, we use router.push for navigation.

BProgress doesn't add click events to router.push, so naturally it won't show a progress bar.

Don't worry, BProgress provides us with a useRouter method.

Replace Next.js's useRouter with the useRouter provided by BProgress:

// import { useRouter } from 'next/navigation';
import { useRouter } from '@bprogress/next/app';

Then use it as normal:

const router = useRouter();

router.push('/');

Now you can see that after successful login, when automatically navigating to the home page, the progress bar displays correctly.

Progress bar displays correctly after using BProgress's useRouter

But if your project has already wrapped its own useRouter, you can pass the wrapped useRouter as a parameter customRouter for a second wrapping:

import { useRouter } from '@bprogress/next/app';
import { useRouter as useNextIntlRouter } from '@/i18n/navigation';

export default function Home() {
const router = useRouter({
customRouter: useNextIntlRouter,
});

return (
<button
onClick={() =>
router.push('/about', {
startPosition: 0.3,
locale: 'en',
})
}
>
Go to about page
</button>
);
}

Finally, let's go back to app/layout.tsx, where we imported ProgressProvider but turned app/layout into a client component. Let's extract ProgressProvider elsewhere and keep app/layout as a server component.

// app/components/ProgressWrapper.tsx
'use client';

import { ProgressProvider } from '@bprogress/next/app';

interface ProgressWrapperProps {
children: React.ReactNode;
}

export function ProgressWrapper({ children }: ProgressWrapperProps) {
return (
<ProgressProvider
height="4px"
color="#0000ff"
options={{ showSpinner: false }}
shallowRouting
>
{children}
</ProgressProvider>
);
}

In app/layout.tsx, we import ProgressWrapper:

import { ProgressWrapper } from './components/ProgressWrapper';

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<ProgressWrapper>
{children}
</ProgressWrapper>
</body>
</html>
);
}

Great job! You've completed the integration of a route navigation progress bar in Next.js.

That's all for this tutorial. I hope you found it helpful.

Thanks for reading! 👏