Intercepting routes let you load a route within the current layout during client-side navigation while preserving the full page on direct URL access or refresh. The classic use case is a modal that opens inline but has its own shareable URL.
Convention Matches
(.)folder Same level (like ./folder)
(..)folder One level up (like ../folder)
(..)(..)folder Two levels up (like ../../folder)
(...)folder Root level (like /folder from anywhere)
app/
├── layout.tsx
├── @modal/
│ ├── default.tsx # Renders nothing when no modal is active
│ └── (.)photo/[id]/page.tsx # Intercepts /photo/:id → renders in modal
├── photo/[id]/
│ └── page.tsx # Full page for /photo/:id (direct access)
└── page.tsx # Gallery page with photo thumbnails
When to reach for this: Photo galleries with modal previews, login modals with /login URLs, item detail previews in a list, or any pattern where a route should show as a modal during navigation but as a full page on direct access.
The dot convention refers to route segments, not file-system directories.(..) goes up one route segment, which may not correspond to one directory level if route groups are involved.
default.tsx must exist in the slot. Without it, navigating away from the intercepted route and then back can cause errors.
Refreshing the page loads the full route, not the intercepted one. Users who share the URL will see the full page, not the modal - this is by design.
Route groups (group) count as a segment for the dot convention. If your intercepting route is inside a route group, you may need an extra (..) level.
Back navigation may not work as expected with complex histories. If the user navigated through multiple intercepted routes, router.back() pops the last entry, which might be another modal.
Intercepting routes add complexity. If you do not need the shareable-URL-as-full-page behavior, a simple client-side modal state is simpler.
Parallel route slots must be direct children of a layout. You cannot nest @modal arbitrarily - it must be a sibling of the layout that renders it.
When does route interception happen and when does it not?
Interception happens only during client-side navigation (clicking a <Link>)
Direct URL access, page refresh, or shared links bypass interception and load the full page version
What do the dot conventions (.), (..), (..)(..), and (...) mean?
(.) matches the same route level
(..) matches one route level up
(..)(..) matches two route levels up
(...) matches from the root level
Why is default.tsx required in the @modal slot?
Without default.tsx, navigating away from the intercepted route and then back can cause errors. It provides a fallback (typically returning null) when no modal is active.
How do you dismiss an intercepted modal?
Call router.back() from the useRouter hook. Since interception uses client-side navigation, going back restores the previous view.
"use client";import { useRouter } from "next/navigation";const router = useRouter();// In the close button:<button onClick={() => router.back()}>Close</button>
Gotcha: Do the dot conventions refer to file-system directories or route segments?
Route segments, not file-system directories. This distinction matters when route groups are involved, because route groups (group) count as a segment for the dot convention and may require an extra (..) level.
Does the intercepting route replace the original route?
No. The original full page still exists at its URL. Interception only shadows it during soft (client-side) navigation. The original renders on direct access or refresh.
What is the relationship between intercepting routes and parallel routes?
Intercepting routes are placed inside parallel route slots (e.g., @modal). The slot is rendered as a prop in the parent layout alongside children, allowing the modal and page to display simultaneously.
What are the TypeScript types for an intercepted route page?
How do you type the params in a Client Component intercepted route?
Use React.use() to unwrap the Promise since you cannot use await in Client Components.
"use client";import { use } from "react";export default function Modal({ params,}: { params: Promise<{ id: string }>;}) { const { id } = use(params); return <div>Item {id}</div>;}
Gotcha: What happens with complex navigation histories and multiple intercepted routes?
router.back() pops the last history entry, which might be another modal rather than the non-modal page. Users navigating through multiple intercepted routes may need to go back multiple times.
When should you use a simple client-side modal instead of intercepting routes?
When you do not need a shareable URL for the modal content. Intercepting routes add file-system complexity. If the modal does not need its own URL or full-page fallback, client-side state is simpler.
Can @modal be nested inside another @slot?
No. Parallel route slots must be direct children of a layout directory. You cannot nest @modal inside another slot.