Next.js Intercepting Routes
Last Updated :
12 Aug, 2024
Next.js is the most widely used React framework. With each new major release, the Next.js team is adding amazing features to the framework. Here, We will deep dive into a feature called Intercepting Routes which was added in a recent release to render modal on the same page with a unique URL.
Intercepting routes involves using a special lifecycle method provided by the framework to execute code before navigating to a different page. This can be useful for tasks like authentication, data fetching, or logging.\
Prerequisites
Parallel Routes in Next.js
Knowledge of parallel routes is important to effectively use intercepting routes. Parallel routes allow you to render one or more pages in the same layout simultaneously or conditionally.
Parallel routes are defined using slots. To create a slot, name your folder with a @
prefix. For example, using @news
in your folder structure creates a slot that can receive props from the shared parent layout. In this case, the root layout acts as the parent layout.
Here’s how you can modify the layout.tsx
file to integrate the slot:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./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,
news,
}: Readonly<{
children: React.ReactNode;
news: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
{news}
{children}
</body>
</html>
);
}
What is default.{jsx | tsx} file?
By default, Next.js keeps track of the active state (or subpage) for each slot. During client-side navigation, Next.js will perform a partial render, changing the subpage within the slot, while maintaining the other slot's active subpages, even if they don't match the current URL. But after browser refresh, Next.js cannot determine the active state for the slots that don't match the current URL. Instead, it will render a default.js file for the unmatched slots, or 404 if default.js doesn't exist.
Intercepting Routes in Next.js
Intercepting routes allows to load of content of another route in current page. Combined with Next.js parallel routes, this is can be used to display modal over current page to show contents of another page.
Example: Clicking an image or button might open a modal on top of the current page. When accessed via a URL or after a hard refresh, it routes to a dedicated page.
We are going to make a similar application where clicking over login button will open a modal. But when accessed using URL, it will open the login page.
Convention for Intercepting routes
Intercepting routes works similar to relative paths. Intercepting routes are defined using (..) convention.
You can use:
- (.) to match segments on the same level
- (..) to match segments one level above
- (..)(..) to match segments two levels above
- (...) to match segments from the root app directory
Important Note:
Although the (..)
convention may resemble relative paths, it differs significantly. The (..)
convention is based on route segments rather than the file system. Each folder in a route represents a route segment, which maps to a corresponding segment in the URL path. Note that parallel routes are not considered route segments because they use slots, which do not affect the URL.
Steps To Implement Next.js Intercepting Routes
Step 1: Copy one of the following command to initialise the the application in your desired directory.
#npm
npx create-next-app@latest
#yarn
yarn create next-app
#pnpm
pnpm create next-app
Options:
Next.js optionsStep 2: Install shadcn ui library.
To install the Shadcn UI library, use one of the following commands based on your package manager:
#npm
npx shadcn-ui@latest init
#yarn
yarn dlx shadcn-ui@latest init
#pnpm
pnpm dlx shadcn-ui@latest init
Shadcn choicesStep 3: Add the following components.
To add the required components, use one of the following commands based on your package manager:
#npm
npx shadcn-ui@latest add button card dialog input label
#yarn
yarn dlx shadcn-ui@latest add button card dialog input label
#pnpm
pnpm dlx shadcn-ui@latest add button card dialog input label
Step 4: Start the Development Server
#npm
npm run start
#yarn
yarn dev
#pnpm
pnpm dev
Dependencies
"dependencies": {
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lucide-react": "^0.403.0",
"next": "14.2.4",
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.4",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
Folder Structure
Folder StructureWe going to make simple application where if user clicks on the login button in home page then a modal will open. But if browser is refreshed or accessed to URL, login page will open.
JavaScript
JavaScript//components/login-form.tsx
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
export function LoginForm() {
return (
<Card className="w-full max-w-sm">
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription>
Enter your email below to login to your account.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" required />
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" required />
</div>
</CardContent>
<CardFooter>
<Button className="w-full">Sign in</Button>
</CardFooter>
</Card>
);
}
Step 2: Create login route in root directory. Here is the code for page.tsx.
JavaScript
//app/login/page.tsx
import { LoginForm } from "@/components/login-form";
export default function Page() {
return (
<main className="w-full h-screen flex
flex-col justify-center items-center gap-7">
<h1 className="text-4xl font-semibold ">Login page</h1>
<LoginForm />
</main>
);
}
Step 3: Create a parallel is same level as root layout and name it @modal.
Step 4: Modify the root layout.tsx to add the slot for parallel route.
JavaScript
//app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./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,
}: Readonly<{
children: React.ReactNode;
modal: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
{modal}
{children}
</body>
</html>
);
}
Step 5: Create required files.
Inside the @modal directory, create (.)login directory and inside that, create page.tsx file. When /login route will be access through next/link component, it will show a modal. Here is the code for modal and page.tsx.
JavaScript
//app/@modal/(.)login/page.tsx
import { LoginForm } from "@/components/login-form";
import { Modal } from "@/components/modal";
export default function Page() {
return (
<Modal>
<LoginForm />
</Modal>
);
}
JavaScript
//components/modal.tsx
"use client";
import { useRouter } from "next/navigation";
import { Dialog, DialogContent, DialogOverlay } from "./ui/dialog";
export function Modal({ children }: { children: React.ReactNode }) {
const router = useRouter();
return (
<Dialog defaultOpen={true} open={true}
onOpenChange={() => router.back()}>
<DialogOverlay>
<DialogContent className="w-fit py-9 px-7">
{children}</DialogContent>
</DialogOverlay>
</Dialog>
);
}
Step 6: Create default.tsx folder
Create default.tsx inside the (.)login directory. It does nothing and only added because Next.js requires it. Otherwise Next.js will render a 404 page.
JavaScript
//app/@modal/default.tsx
export default function Default() {
return null;
}
Step 7: At last, modify the root page.tsx. Clicking on the link link component will open the modal.
JavaScript
//app/page.tsx
import Link from "next/link";
export default function Home() {
return (
<main className="w-full h-screen flex flex-col items-center gap-7 p-20">
<h1 className="text-3xl font-semibold">Home page</h1>
<Link
href="/login"
className="bg-black text-white px-4 py-2 font-semibold rounded-sm"
>
Login
</Link>
</main>
);
}
Now our project is completely ready and should work properly. Restart the dev server otherwise it will not work. This is a bug present in Next.js version 14.2.3.
Output