Published: April 21 2023

Next.js Router - Listen to route (location) change with useRouter

Tutorial built with Next.js 13

This is a quick post on how to detect route changes with Next.js Router to execute code on location change in a Next.js (React) client app.

There are a couple of ways to do it, with a useEffect dependency or with route change events.

  • Detect route change with useEffect dependency: a React useEffect() hook function with the dependency array set to [router] will execute on route change and when the component first loads.
  • Detect route change with router events: the Next.js router allows you to subscribe to route change events including routeChangeStart and routeChangeComplete.

Below are examples of each approach.


Detect route change with Next.js [router] dependency

This snippet shows how to listen to route change with Next.js router by passing the router object in the dependency array of a useEffect() hook function.

The callback function is executed when the component first loads and on route change, it simply increments a route change counter and a logs the current pathname to the console.

NOTE: With [router] as the dependency the code will execute even if you navigate to the same page (e.g. if you click the Home link from the home page). If you only want to execute only when changing to a different route use [router.pathname] instead.

const router = useRouter();

// detect route change with useEffect dependency
const [count, setCount] = useState(0);
useEffect(() => {
    setCount(x => x + 1);
    console.log('route change with dependency', router.pathname);
}, [router]);


Detect route change with Next.js routeChangeStart and routeChangeComplete events

This snippet shows how to subscribe to the routeChangeStart and routeChangeComplete events of the Next.js router.

The useEffect hook with an empty dependency array is executed once when the component is mounted. The return function is executed when the component is destroyed so is used to unsubscribe from the route change events to prevent memory leaks in the app. Named functions are used to enable unsubscribing.

The functions called on route change start and route change complete simply increment a counter for each event and log the current url to the console.

const router = useRouter();
    
// detect route change with router events
const [startCount, setStartCount] = useState(0);
const [completeCount, setCompleteCount] = useState(0);
useEffect(() => {
    // subscribe to routeChangeStart event
    const incrementStartCount = (url) => {
        setStartCount(x => x + 1);
        console.log('routeChangeStart', url);
    };
    router.events.on('routeChangeStart', incrementStartCount);

    // subscribe to routeChangeComplete event
    const incrementCompleteCount = (url) => {
        setCompleteCount(x => x + 1);
        console.log('routeChangeComplete', url);
    };
    router.events.on('routeChangeComplete', incrementCompleteCount);

    // unsubscribe on component destroy in useEffect return function
    return () => {
        router.events.off('routeChangeStart', incrementStartCount);
        router.events.off('routeChangeComplete', incrementCompleteCount);
    }
}, []);


Next.js (React) component that listens to route (location) change

This is the complete Next.js (React) component that the above snippets are taken from, it includes a nav bar and a couple of routes to test the location change listeners.

You can see the code running and tinker with it yourself on StackBlitz at https://stackblitz.com/edit/nextjs-listen-to-route-change.

import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import Link from 'next/link';

import '../styles/globals.css';

export default App;

function App({ Component, pageProps }) {
    const router = useRouter();

    // detect route change with useEffect dependency
    const [count, setCount] = useState(0);
    useEffect(() => {
        setCount(x => x + 1);
        console.log('route change with dependency', router.pathname);
    }, [router]);

    // detect route change with router events
    const [startCount, setStartCount] = useState(0);
    const [completeCount, setCompleteCount] = useState(0);
    useEffect(() => {
        // subscribe to routeChangeStart event
        const incrementStartCount = (url) => {
            setStartCount(x => x + 1);
            console.log('routeChangeStart', url);
        };
        router.events.on('routeChangeStart', incrementStartCount);

        // subscribe to routeChangeComplete event
        const incrementCompleteCount = (url) => {
            setCompleteCount(x => x + 1);
            console.log('routeChangeComplete', url);
        };
        router.events.on('routeChangeComplete', incrementCompleteCount);

        // unsubscribe on component destroy in useEffect return function
        return () => {
            router.events.off('routeChangeStart', incrementStartCount);
            router.events.off('routeChangeComplete', incrementCompleteCount);
        }
    }, []);

    return (
        <div>
            <nav className="navbar navbar-expand navbar-dark bg-dark px-3">
                <div className="navbar-nav">
                    <Link href="/" className="nav-item nav-link">Home</Link>
                    <Link href="/about" className="nav-item nav-link">About</Link>
                </div>
            </nav>
            <div className="container pt-4 pb-4">
                <Component {...pageProps} />
                <hr />
                <ul>
                    <li>Route change count with useEffect dependency: {count}</li>
                    <li>routeChangeStart event count: {startCount}</li>
                    <li>routeChangeComplete event count: {completeCount}</li>
                    <li>Current route: {router.pathname}</li>
                </ul>
            </div>
        </div>
    );
}

 


Need Some NextJS Help?

Search fiverr for freelance NextJS developers.


Follow me for updates

On Twitter or RSS.


When I'm not coding...

Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!


Comments


Supported by