Open In App

Next.js Intercepting Routes

Last Updated : 12 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

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:

240709_18h02m31s_screenshot
Next.js options

Step 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
240709_18h04m04s_screenshot
Shadcn choices

Step 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

240709_18h19m40s_screenshot
Folder Structure

We 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.

Step 1: Create a file in components directory of the for login form

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;
}
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


Next Article

Similar Reads