How to quickly listen for a route/page change in Next.js

Last updated on September 30, 2023
How to quickly listen for a route/page change in Next.js

To detect a route change in Next.js:

  • app directory: use useEffect and usePathname.
  • pages directory: use useEffect and useRouter.

Listen for route/page change in Next.js app directory

To listen for a route change in Next.js 13 app directory, use the useEffect and the usePathname hooks. The action in useEffect will run anytime the pathname from usePathname changes.

'use client';

import { usePathname } from 'next/navigation';
import { useEffect, useState } from 'react';

export function RouteChangeListener() {
  const pathname = usePathname();
  const [changes, setChanges] = useState(0);

  useEffect(() => {
    console.log(`Route changed to: ${pathname}`);
    setChanges((prev) => prev + 1);
  }, [pathname]);

  return <></>;
}

Here we log to the console and change the state of the component when the route changes.

The URL change listener logs to the console when the route changes.
The URL change listener logs to the console when the route changes.

Page change listener must persist with client-side routing

For this detection to work, the component containing this useEffect needs to be somewhere in the DOM where it will not get unmounted with client-side navigation.

In the Next.js 13 app directory, this could be the layout.tsx file:

import { Metadata } from 'next';
import '@/styles/globals.css';
import { RouteChangeListener } from './route-change-listener';

export const metadata: Metadata = {
  title: 'Coding Beauty',
  description: 'The official Coding Beauty home page.',
  icons: {
    icon: '/favicon.png',
  },
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      {/* 👇 Persists with client-side navigation */}
      <RouteChangeListener />
      <body>{children}</body>
    </html>
  );
}

useEffect needs 'use client'

Also, since server components are the default in Next.js 13, you'll need to add the 'use client' directive at the top of the component file.

Otherwise, you won't be able to use interactive client-side features like React hooks, including useEffect.

The useEffect React hook can't work without the 'use client' directive in Next.js.
useEffect can't work without 'use client' in Next.js.

Listen for route/page change in Next.js pages directory

To handle a URL or location change in Next.js pages directory, combine the useEffect and the useRouter hooks:

import { useEffect } from 'react';
import { useRouter } from 'next/router';

export function UrlChangeListener() {
  const router = useRouter();

  useEffect(() => {
    console.log(`The page is now: ${router.pathname}`);
  }, [router]);

  return <></>;
}

Route change detector must persist with client-side routing

Just like with the app directory, the component that listens for the URL change with useEffect needs to be somewhere in the DOM where it will not get unmounted with client-side navigation.

In the pages directory, this could be the _app.tsx or _app.js file:

import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import { UrlChangeListener } from './url-change-listener';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <UrlChangeListener />
      <Component {...pageProps} />
    </>
  );
}

Detect route/page with useRouter() events

Alternatively, we can detect a client-side URL change in the Next.js pages directory with events that the useRouter() object emits. For example:

import { useEffect } from 'react';
import { useRouter } from 'next/router';

export function UrlChangeListener() {
  const router = useRouter();

  useEffect(() => {
    const startHandler = () => {
      console.log('Router change started');
    };

    const completeHandler = () => {
      console.log('Router change completed');
    };

    router.events.on('routeChangeStart', startHandler);
    router.events.on('routeChangeComplete', completeHandler);

    return () => {
      router.events.off('routeChangeStart', startHandler);
      router.events.off('routeChangeComplete', completeHandler);
    };
  }, []);

  // 👇 You can put a progress bar or something here
  return <></>;
}

Here we used two important router events:

  • routeChangeStart - fires when the route is about to change.
  • routerChangeComplete - fires when the router has changed completely.

There's more too, and their names are just as self-explanatory as these two.

The `useRouter()` object has other properties apart from routeChangeStart and routeChangeComplete.

Key takeaways

  • To listen for a route or page change in Next.js app directory, combine the useEffect and usePathname hooks.
  • Detect a URL change in the pages directory with the useEffect and useRouter hooks.
  • You can also use the routeChangeStart and routeChangeComplete events from the useRouter() object to handle a location change.

See also