Skip to main content

2 posts tagged with "Next.js"

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! 👏

Next.js 路由跳转显示进度条:使用 BProgress 提升用户体验

· 5 min read
吴楷鹏
前端开发工程师

本期已录制 B 站视频 👉 【Next.js】路由跳转显示进度条

哈喽,我是楷鹏。

先来看一个反面教材。

在 Dify.ai 中,当点击跳转页面之后,会有一段需要等待的时间,然后才会跳转页面。

Dify.ai 页面跳转时缺少进度反馈

然而,中间这段时间我并不知道是否跳转成功了,所以我会多点了几下,直到跳转。

这种体验很不好 👎

解决方案很简单,我们来看一下 GitHub 的跳转交互。

GitHub 页面跳转时的进度条效果

可以看到,GitHub 在跳转期间,会显示一个进度条,清晰地告诉用户——"我正在跳转,请稍等"。

那么在 Next.js 中,如何实现这个效果呢?

我们可以借助 BProgress 这个库来实现。

BProgress 官网首页展示

BProgress 是一个轻量级的进度条组件库,支持 Next.js 15+,同时也支持 Remix、Vue 等其他框架。

对于 BProgress 的使用,我做了一个 demo 项目 nextjs-progress-bar-demo,我们可以把这个项目先 clone 下来:

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

然后进入项目目录:

cd nextjs-progress-bar-demo

先安装依赖:

npm install @bprogress/next

启动项目:

npm run dev

Next.js 进度条演示项目界面

可以看到,这是一个简单的 Next.js 项目,包含三个页面:首页、登录页、注册页。

main 分支已经配置好了进度条,我们切换到分支 without-progress-bar-demo

git checkout without-progress-bar-demo

当前分支下,我们没有配置进度条,所以跳转页面时,不会显示进度条。

接下来我们在根布局 app/layout.tsx 中引入 ProgressProvider

'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>
);
}

接下来,我们可以看一下,在首页和登录页、登录页和注册页之间跳转,都会显示一个进度条。

集成 BProgress 后的页面跳转效果

ProgressProvider 的参数如下:

  • height:进度条的高度
  • color:进度条的颜色
  • options:进度条的配置,这里 showSpinner 设置为 false,表示不显示一个动画的加载图标。
  • shallowRouting:是否启用浅层路由,如果开启的话,只改变路由的 query 参数,比如 ?page=1 变成 ?page=2,那么进度条不会重新加载。

但是,当我们登录成功之后,再点击跳转,却不会显示进度条。

使用 router.push 跳转时进度条未显示

这是因为,首页和登录页、登录页和注册页之间,是使用 <Link> 组件进行跳转的。

<Link> 组件实际会渲染成 <a>,BProgress 通过给所有 <a> 组件添加点击事件,来显示进度条。

我们可以看下在 DevTools → Elements → <a> → Event Listeners 中,是否添加了点击事件:

DevTools 中查看 Link 组件的点击事件监听器

但是,当我们登录成功之后,则是使用 router.push 进行跳转的。

BProgress 不会给 router.push 添加点击事件,自然也不会显示进度条。

不用慌,BProgress 为我们提供了 useRouter 方法。

将 Next.js 的 useRouter 替换为 BProgress 提供的 useRouter

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

然后,正常使用即可:

const router = useRouter();

router.push('/');

这时,你可以看到,在登录成功之后,自动跳转首页时,进度条就能正常显示了。

使用 BProgress 的 useRouter 后进度条正常显示

但如果你的项目已经封装过了自己的 useRouter,那么你可以将封装过的 useRouter 作为参数 customRouter 传入,进行二次封装:

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

最后,让我们回到 app/layout.tsx,这里我们引入了 ProgressProvider,但却把 app/layout 变成了一个客户端组件,我们来把 ProgressProvider 抽离到其他地方,仍然保持 app/layout 是一个服务端组件。

// 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>
);
}

app/layout.tsx 中,我们引入 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>
);
}

好的,不愧是你,完成了一个 Next.js 集成路由跳转显式进度条的封装。

以上就是本期的全部内容,希望对你有所帮助。

感谢观看!👏