How to integrate NProgress with Next.js
Snippets for showing a top loading bar while Next.js navigates between pages, using NProgress wired to the Pages Router events.
- react
- next.js
- snippets
This article was written in 2021 for Next.js with the Pages Router. If your project is on the App Router (Next.js 13+),
router.eventsno longer exists: reach fornextjs-toploaderor lean onloading.tsx+Suspenseinstead. The snippets below still apply to legacy Pages Router projects.
If you’ve used Next.js, you’ve probably noticed that navigation between pages isn’t always instant: the router needs a moment to fetch the destination page’s data. A small top loading bar is a low-cost way to tell the user something is happening.
Here’s how to integrate NProgress with Next.js (Pages Router).
1. Install NProgress
pnpm add nprogress
pnpm add -D @types/nprogress2. Wire it into the router events in _app.tsx
import * as React from 'react';
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import NProgress from 'nprogress';
function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();
React.useEffect(() => {
const handleStart = () => NProgress.start();
const handleStop = () => NProgress.done();
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeComplete', handleStop);
router.events.on('routeChangeError', handleStop);
return () => {
router.events.off('routeChangeStart', handleStart);
router.events.off('routeChangeComplete', handleStop);
router.events.off('routeChangeError', handleStop);
};
}, [router]);
return <Component {...pageProps} />;
}
export default MyApp;3. Style the bar and spinner
/* globals.css */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: rgba(167, 139, 250, 0.8);
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 4px;
}
/* glow at the leading edge */
#nprogress .peg {
display: block;
position: absolute;
right: 0;
width: 100px;
height: 100%;
box-shadow:
0 0 10px rgba(167, 139, 250, 0.8),
0 0 5px rgba(167, 139, 250, 0.8);
opacity: 1;
transform: rotate(3deg) translate(0, -4px);
}
/* Drop this block if you don't want 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: rgba(167, 139, 250, 0.8);
border-left-color: rgba(167, 139, 250, 0.8);
border-radius: 50%;
animation: nprogress-spinner 400ms linear infinite;
}
@keyframes nprogress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}That’s it: a small purple bar will show up at the top whenever the router transitions. Tiny detail, big UX win.
Thanks for reading.