How to Listen to URL Changes in Next.js for a Route or Overall App

Nitish Kumar Singh

Nov 23, 2023

Hello Developers, In this blog post, we will see how to listen to URL changes at the route level or application level in a Next.js app. We will create a listener to listen for URL changes in both App Router and Pages Router.

Photo by Thom Holmes on Unsplash

To listen for URL changes, we use hooks and create components that handle all the things. So, we use the following hooks for both Routers:

  • Pages Router: In Pages Router, we use useRouter from next/router and useEffect from react with router as a dependency.
  • App Router: In App Router, we use usePathname from next/navigation and useEffect from react with pathname as a dependency.

Basically, we need a listener at the route level if the route is a dynamic route because once you navigate to that route and change the path of the browser URL by links or using the router object, we need a listener. For example, when I am creating a search form and a search route that shows search results, here I need it. You can see it on my movie streaming website.

Listen to URL Changes in Pages Router

At Route Level

In Pages Router, to listen to URL changes at the route level of a dynamic route, we create a React functional component that takes a function as props to notify the parent route component of URL changes and use hooks for listening. And render this component in that route component for which we want to listen to URL changes. So the following code is an example of a Listener component to listen at the route level:

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

export default function Listener({onUrlChange}) {
    const router = useRouter();
    useEffect(()=>{
        // Get path that showing on browser url
        console.log(router.asPath);
        // Get query object and query parameter.
        console.log(router.query.collection);
        // notify dynamic(parent) route with path
        onUrlChange(router.asPath);
    },[router,onUrlChange]);
  return ("")
}

Here I have created a dynamic route by naming the folder [collection], so getting the query (or what you say it) as the collection property of the query object.

Render the above component in that dynamic (parent) route like showing in the below code:

import Listener from "...";

export default class Collection extends Component {
  onUrlChange = (pathname)=>{
    console.log(pathname);
  }
  render() {
    return (
      <>
         <Listener onUrlChange={this.onUrlChange}/>
         {/* below your rest of jsx*/}
         ....
      </>
    )
  }
}

I use it in a class component, but you can also use it in functional components.

At Application Level

To listen at the application level, we create a GlobalListener like the previous listener, just here change notifying code and render it in the _app component.

The following code is an example of that component:

import { useRouter } from 'next/router'
import { useEffect } from 'react'
export const listners = {};
export default function GlobalListener() {
    const router = useRouter();
    useEffect(()=>{
        const pathname = router.asPath;
        for (const key in listners) {
            listners[key](pathname);
        }
    },[router]);
  return ("")
}

And render it in the _app component like below:

import '@/styles/globals.css'
mport GlobalListener from './Componets/Listener';

export default function App({ Component, pageProps }) {
  return (
    <>
    <GlobalListener/>
    <Component {...pageProps} />
    </>
  )
}

Whenever we need to listen for URL changes in a route or component, then attach a listener (function) with a unique name like below:

import { listners } from './Componets/Listener';

const onUrlChange = (pathname)=>{
   console.log(pathname);
}
    
listners.hello = this.onUrlChange;

And make sure to remove it if not needed like below:

// in class component
componentWillUnmount(){
    delete listners.hello;
}

Listen to URL Changes in App Router

In App Router, we do all things the same as doing for Pages Router, just change a few things like below:

  • First of all, we use the "use client" directive in the Listener component.
  • For listening to URL changes, we use usePathname from next/navigation and useEffect from react with pathname as a dependency.
  • And render it in layout.js file for global (app level) listening.

So the following code is an example of the listener for listening at the route level in App Router:

'use client';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react'
export default function Listener({onUrlChnage}) {
    const pathname = usePathname();
    useEffect(()=>{
        onUrlChnage(pathname);
    },[pathname]);
  return ("")
}

And for application level:

'use client';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react'
export const listners = {};
export default function GlobalListener() {
    const pathname = usePathname();
    useEffect(()=>{
        for (const key in listners) {
            listners[key](pathname);
        }
    },[pathname]);
  return ("")
}

Render it in layout.js file of the app directory like below:

import "./globals.css";
import GlobalListener from "....";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <GlobalListener/>
        <div id="root">
           {children}
        </div>
      </body>
    </html>
  );
}

Attach and detach listeners the same as Pages Router.

I hope you learn how to listen for URL changes in Next.js and enjoy reading this blog post. Feel free to modify the above codes as your requirements. I just described what I created and used on my movie streaming site.

Published on Nov 23, 2023
Comments (undefined)

Read More