Next.js Routing: Intercepting Routes
Next.js's app router introduces an advanced routing pattern: intercepting routes. Lets jump into this routing type:
What are the Intercepting Routes?
Sometimes, you may want to give users quick access to a page by opening it in a modal without fully navigating away from the current page. At other times, you might not want to show the the target page at all but still you may want to avoid changing the route, page structures or you may want to avoid extra work. Next.js has a solution, it is intercepting routes.
Intercepting routes allow you to display content from another page, like a modal, without navigating away from the current view. In this case, the intercepted page still exists, and refreshing the intercepted route will display the content directly. Sharing the link will also give direct access to that page.
How Intercepting Routes Work?
Intercepting routes in Next.js are based on route segments, not the file system. We can use the following conventions for route matching:
(.) // Matches segments at the same level. (..) // Matches segments from one level up. (...) // Matches segments from the root app directory.
Let’s say you want to intercept the
photo
segment from within the feed
segment, and show something else to the user. You can define the route as:/feed/(..)photo/page.tsx
And you can put whatever you want to display on the
page.tsx
. Don't forget: the intercepted root is still accessible by a refresh or link sharing.Still Not Working?: After making changes to intercepting routes, you may not see the changes instantly, you can restart the server, do a hard refresh, or delete the
.next
folder to see the changes.Using Dynamic Routes with Interception
You can also create dynamic intercepting routes like this and intercept them:
/feed/(..)photo/[id]/page.tsx
Combining Parallel Routes and Intercepting Routes
Let's take another example using parallel routes. I'll cover parallel routes in more detail in another article, but for now, let’s combine them with intercepting routes.
Imagine we have two segments:
feed
(an image gallery) and photo
(photo details). Clicking on an image in the feed opens a modal. Here’s what the route structure looks like:/feed/@modal/(..)photo/[id]/page.tsx
Here, the
@modal
folder is a named slot used to create parallel routes, not an actual route segment. The (..)photo/[id]
route intercepts and opens the modal from the feed
page, without changing the full page. In page.tsx
, we define the modal component. Additionally, in app/layout.tsx
, we need to include the modal so it can be rendered properly across the app.To ensure proper fallback behavior, in the
(..)photo
segment, we should add a default.tsx
file. This file will return null
when nothing is intercepted, preventing any unwanted content from being displayed when the route isn't active.Let's add a modal for our page:
import { useRouter } from 'next/router'; import { useEffect } from 'react'; const PhotoModal = () => { const router = useRouter(); const { id } = router.query; useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { router.back(); } }; window.addEventListener('keydown', handleEscape); return () => window.removeEventListener('keydown', handleEscape); }, [router]); const closeModal = () => { router.back(); }; return ( <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <div className="bg-white p-6 rounded-lg shadow-lg relative"> <button onClick={closeModal} className="absolute top-2 right-2 text-gray-500 hover:text-gray-800" > × </button> <h1 className="text-xl font-semibold mb-4">Photo ID: {id}</h1> </div> </div> ); }; export default PhotoModal;
Here how we handle the modal:
// layout.tsx import { ReactNode } from 'react'; interface LayoutProps { children: ReactNode; modal: ReactNode; } const Layout = ({ children, modal }: LayoutProps) => { return ( <div> <main>{children}</main> {modal} </div> ); }; export default Layout;
//default.tsx const Default = () => { return null; }; export default Default;
Why Use Intercepting Routes?
Using intercepting routes with modals provides solutions to several common challenges that comes with modals:
Shareable URLs: The modal's content is accessible via a unique URL, making it easy to share with others while preserving the current page state.
Context Retention on Refresh: Even if the page is refreshed, the modal stays open, ensuring that users don’t lose their place or context.
Navigation: When users navigate back, the modal automatically closes, returning them to the previous page instead of causing a full redirect. The modal reopens when navigating forward, maintaining a smooth user experience.
*https://nextjs.org/docs/app/building-your-application/routing/intercepting-routes
Ezgi Ergün - Front-end Developer @ Startbase