How to Listen to URL Changes in Next.js for a Route or Overall App
Nitish Kumar Singh
Nov 23, 2023Hello 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.
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
fromnext/router
anduseEffect
fromreact
withrouter
as a dependency. - App Router: In App Router, we use
usePathname
fromnext/navigation
anduseEffect
fromreact
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
fromnext/navigation
anduseEffect
fromreact
withpathname
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.