Course
Intercepting Routes
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Intercepting Routes
Intercepting routes allow you to intercept other route addresses and display content within the current route.
Consider this example. Open
dribbble.com
, where you’ll find numerous beautiful images:
Click on any image:
A modal will appear, showing the specific content of that image. If you want to view other images, close the modal using the button in the top right corner and continue browsing. Notice that the route address changes to reflect the specific image. If you like an image, you can copy its address and share it with your friends.
When your friend opens the link, instead of a modal, the specific content of the image will be shown directly. Refreshing the page reveals a different layout:
This layout doesn’t use a modal; it simply shows the image content.
The same route address displays different content. This is the effect of intercepting routes. On
dribbble.com
, when you visit dribbble.com/shots/xxxxx
, the address is intercepted and shown as a modal. However, directly accessing dribbble.com/shots/xxxxx
shows the original layout.
Here’s a schematic diagram:
Implementation
To achieve this in Next.js, name folders with
(..)
, where:- (.) matches the same level
- (..) matches one level up
- (..)(..) matches two levels up
- (...) matches the root directory
This matches the route hierarchy, not the folder path hierarchy. Folders like route groups and parallel routes that don’t affect the URL aren’t counted in the hierarchy.
Example:
/feed/(..)photo
corresponds to the route /feed/photo
, intercepting the route /photo
as they are one level apart, so use (..)
.Example Code
Let’s create a demo. The directory structure is:
app├─ layout.js├─ page.js├─ data.js├─ default.js├─ @modal│ ├─ default.js│ └─ (.)photo│ └─ [id]│ └─ page.js└─ photo └─ [id] └─ page.js
First, mock some image data.
app/data.js:
export const photos = [ { id: '1', src: 'https://i.imgur.com/F6fgWJH.png' }, { id: '2', src: 'https://i.imgur.com/SHM7MVG.png' }, { id: '3', src: 'https://i.imgur.com/tCMx39R.png' }, { id: '4', src: 'https://i.imgur.com/uIPx8QK.png' }, { id: '5', src: 'https://i.imgur.com/YJ30ogJ.png' }, { id: '6', src: 'https://i.imgur.com/fEExg9R.png' }, { id: '7', src: 'https://i.imgur.com/YsOT6vr.png' }, { id: '8', src: 'https://i.imgur.com/Zw1DjqO.png' },];
app/page.js:
import { Boundary } from '@/components/boundary';import Link from 'next/link';import { photos } from './data';import { DialogTrigger } from '@/components/dialog';
export default async function IndexPage() { return ( <section className="space-y-6 p-4 md:pb-12 md:pt-10 lg:py-20 mx-20"> <Boundary labels={['Root Page']} size="small"> <div className="container flex max-w-[64rem] flex-col items-center gap-4 text-center px-14"> <div className="flex flex-row flex-wrap"> {photos.map(({ id, src }) => ( <Link key={id} href={`/photo/${id}`}> <img width="200" src={src} className="m-1 rounded-lg" /> </Link> ))} </div> </div> </Boundary> </section> );}
app/layout.js:
import type { Metadata } from 'next';import { Inter } from 'next/font/google';import { Header } from '@/components/header';import { SiteFooter } from '@/components/site-footer';import '@/styles/globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = { title: 'Create Next App', description: 'Generated by create next app',};
export default function RootLayout({ children, modal,}: { children: React.ReactNode; modal: React.ReactNode;}) { return ( <html lang="en"> <body className={inter.className}> <div className="flex min-h-screen flex-col"> <Header /> <main className="flex-1"> {children} {modal} </main> <SiteFooter /> </div> </body> </html> );}
Visiting
/
results in:
Next, implement the effect for direct image address access.
Create app/photo/[id]/page.js:
import { photos } from '../../data';import { notFound } from 'next/navigation';
interface Props { params: { id: string; };}
export default function PhotoPage({ params }: Props) { const photo = photos.find((p) => p.id === params.id);
if (!photo) { notFound(); }
return ( <img className="block w-1/4 mx-auto mt-10 rounded-lg" src={photo.src} /> );}
Visiting
/photo/6
results in:
Now implement intercepting routes. For a different style effect, declare it separately.
app/@modal/(.)photo/[id]/page.js:
import { photos } from '../../../data';import { notFound } from 'next/navigation';import Modal from '@/components/modal';
interface Props { params: { id: string; };}
export default function PhotoModal({ params }: Props) { const photo = photos.find((p) => p.id === params.id);
if (!photo) { notFound(); }
return ( <Modal> <img className="w-full h-full rounded-lg" src={photo.src} /> </Modal> );}
Since we’re using parallel routes, set default.js in both app/default.js and app/@modal/default.js:
export default function Default() { return null}
Playground:
When visiting
/
and accessing /photo/5
, the route is intercepted and uses the style from @modal/(.)photo/[id]/page.js
.Summary
Well done! In this section, we covered dynamic routes, route groups, parallel routes, and intercepting routes. They all involve modifying file names. Dynamic routing manages dynamic links, route groups organize code, while parallel routes and intercept routes handle real-world development situations. grasping parallel and intercept routes might be tough initially but by following the demos in the article and typing them out yourself, you'll soon grasp and excel at them! 👏 👏 👏