---
title: "09 — Parallel and Intercepting Routes — Advanced App Router Patterns"
uid: advanced-routing-patterns
tags: ["roadmap:nextjs", "parallel-routes", "routing", "app-router", "nextjs", "intercepting-routes"]
excerpt: "Parallel routes render two pages side by side in one layout; intercepting routes load a route inside another's layout — think modal-over-feed."
date: 2026-08-13T03:28:01+0000
source: https://www.aveshina.my.id/en/blog/advanced-routing-patterns
---

Two patterns make the App Router genuinely expressive, and both were hard under the old router. The model that clicked: **parallel routes render two or more pages simultaneously in the same layout (independent navigation); intercepting routes load a route from elsewhere inside the current layout (modal-over-feed, modals that share a URL)** [1]. Both unlock UI patterns that were genuinely hard under the Pages Router.

## Parallel routes — multiple pages, one view

Parallel routes let me simultaneously or conditionally render one or more pages within the same layout [2]. They're named with the @ prefix — @analytics, @team — and passed to the layout as props alongside children:

```
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
  @analytics: analytics,
  @team: team,
}: {
  children: React.ReactNode;
  @analytics: React.ReactNode;
  @team: React.ReactNode;
}) {
  return (
    <div className="grid grid-cols-2">
      <div>{analytics}</div>
      <div>{team}</div>
      <div className="col-span-2">{children}</div>
    </div>
  );
}
```

Each slot (@analytics, @team) is its own route that navigates independently. The classic use case the roadmap calls out: dashboards and feeds on social sites, where each panel can load, reload, and navigate without disturbing the others [2]. A slow panel doesn't block the fast ones, because each is its own Suspense boundary.

```figure
<svg viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Parallel routes. A dashboard layout with three slots: @analytics (top-left panel), @team (top-right panel), and children (bottom, full width). Each slot navigates independently.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <text x="360" y="24" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">Parallel routes — one layout, independently-navigating slots</text>

    <!-- layout frame -->
    <rect x="60" y="40" width="600" height="220" rx="10" fill="#f8fafc" stroke="#64748b" stroke-width="1.5"/>
    <text x="80" y="62" font-size="11" font-weight="700" fill="#475569">app/dashboard/layout.tsx</text>

    <!-- @analytics slot -->
    <rect x="80" y="78" width="270" height="100" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="215" y="100" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">@analytics</text>
    <text x="215" y="120" font-size="10" fill="#475569" text-anchor="middle">navigates independently</text>
    <text x="215" y="158" font-size="10" font-style="italic" fill="#1e1b4b" text-anchor="middle">own Suspense, own loading</text>

    <!-- @team slot -->
    <rect x="370" y="78" width="270" height="100" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="505" y="100" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">@team</text>
    <text x="505" y="120" font-size="10" fill="#475569" text-anchor="middle">navigates independently</text>
    <text x="505" y="158" font-size="10" font-style="italic" fill="#052e16" text-anchor="middle">own Suspense, own loading</text>

    <!-- children -->
    <rect x="80" y="190" width="560" height="56" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="360" y="214" font-size="12" font-weight="700" fill="#422006" text-anchor="middle">children (default slot)</text>
    <text x="360" y="232" font-size="10" fill="#475569" text-anchor="middle">the main route</text>
  </g>
</svg>
```

## Intercepting routes — load one route inside another's layout

Intercepting routes let me load a route from another part of the application *within the current layout* [3]. The classic example: clicking a photo in a feed opens a modal overlay showing that photo, without leaving the feed context. The (..) convention in the folder name marks interception — (..)photo intercepts the photo route from one level up.

```
app/
├── feed/
│   └── (..photo)/
│       └── [id]/page.tsx   ← intercepts /photo/[id], shows it as a modal over the feed
└── photo/
    └── [id]/page.tsx        ← the real /photo/[id] route (direct visit)
```

The two behaviors matter:

- **Client-side navigation** (clicking the photo from the feed) → the intercepted route renders as a modal, preserving the feed underneath.
- **Direct URL visit** (loading /photo/123 fresh) → the real, un-intercepted route renders as a standalone page.

This is the pattern that made modals-with-URLs tractable. The roadmap's framing is exactly right: it lets me display a route's content without switching context [3]. Before intercepting routes, "shareable modal with its own URL" was surprisingly hard.

## How I use this

Parallel routes are my go-to for dashboards — any UI with independent panels that load or navigate separately. Intercepting routes are my answer for modal-over-list patterns where the modal should be deep-linkable: photo viewers, detail drawers, comment threads opened from a feed. Both patterns turn "hard routing problem" into "folder convention," which is the App Router's whole value proposition.

## References

[1] Vercel, "Advanced routing patterns," Next.js Docs, 2023. [Online]. Available: [https://nextjs.org/docs/13/app/building-your-application/routing#advanced-routing-patterns](https://nextjs.org/docs/13/app/building-your-application/routing#advanced-routing-patterns)

[2] Vercel, "Parallel routes," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/api-reference/file-conventions/parallel-routes](https://nextjs.org/docs/app/api-reference/file-conventions/parallel-routes)

[3] Vercel, "Intercepting routes," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/api-reference/file-conventions/intercepting-routes](https://nextjs.org/docs/app/api-reference/file-conventions/intercepting-routes)

[4] "Next.js 15 tutorial — Parallel routes," YouTube, 2024. [Video]. Available: [https://www.youtube.com/watch?v=697kNwfU-4M](https://www.youtube.com/watch?v=697kNwfU-4M)

[5] "Next.js 15 tutorial — Intercepting routes," YouTube, 2024. [Video]. Available: [https://www.youtube.com/watch?v=FTiwIVxWC00](https://www.youtube.com/watch?v=FTiwIVxWC00)

```quiz
Q: What do parallel routes (the @-prefixed folders) let you do?
- Load the same page twice for A/B testing
- Render two or more pages simultaneously in one layout, each navigating independently
correct: 1
explain: Parallel routes render multiple pages side by side via named slots (@analytics, @team) passed to the layout. Each slot is its own route with independent navigation and its own Suspense boundary.

Q: The classic use case for intercepting routes is…
- hiding routes from the URL bar
- showing a route's content inside another route's layout — e.g., a photo modal over a feed, while keeping a shareable URL
correct: 1
explain: Intercepting routes let you load one route within another's context. Clicking a photo in a feed can open it as a modal over the feed, while a direct URL visit renders the standalone page.

Q: With intercepting routes, what's the difference between client-side navigation and a direct URL visit?
- No difference — both render identically
- Client-side navigation renders the intercepted (modal) version; a direct visit renders the real standalone route
correct: 1
explain: The interception only kicks in on client-side navigation from the intercepting context. Loading the URL directly bypasses the interception and renders the actual route.

Q: Why are parallel routes well-suited to dashboards?
- They make every panel the same size
- Each panel is an independent route with its own loading/error state, so a slow panel doesn't block the others
correct: 1
explain: Because each slot navigates and loads independently with its own Suspense boundary, slow panels don't block fast ones — exactly what dashboards with heterogeneous data sources need.
```
