Skip to main content

Parallel and Intercepting Routes

Answer

Parallel Routes allow rendering multiple pages in the same layout simultaneously. Intercepting Routes let you show a route within another route's context, useful for modals.

Parallel Routes

app/
├── layout.js
├── page.js
├── @sidebar/
│ └── page.js
└── @main/
└── page.js
// app/layout.js
export default function Layout({ children, sidebar, main }) {
return (
<div className="flex">
<aside>{sidebar}</aside>
<main>{main}</main>
</div>
);
}

Parallel Routes for Modals

app/
├── layout.js
├── page.js
├── @modal/
│ ├── default.js # Shown when no modal
│ └── (.)photo/[id]/
│ └── page.js # Modal content
└── photo/
└── [id]/
└── page.js # Full page
// app/layout.js
export default function Layout({ children, modal }) {
return (
<>
{children}
{modal} {/* Modal slot */}
</>
);
}

// app/@modal/default.js
export default function Default() {
return null; // No modal by default
}

Intercepting Route Conventions

(.)  → Same level
(..) → One level up
(..)(..) → Two levels up
(...) → From root

Photo Modal Example

// app/@modal/(.)photo/[id]/page.js
import { Modal } from "@/components/modal";

export default function PhotoModal({ params }) {
return (
<Modal>
<img src={`/photos/${params.id}.jpg`} />
</Modal>
);
}
// app/photo/[id]/page.js (Full page)
export default function PhotoPage({ params }) {
return (
<div className="photo-page">
<img src={`/photos/${params.id}.jpg`} />
<Comments photoId={params.id} />
</div>
);
}

How It Works

"use client";
import { useRouter } from "next/navigation";

export function Modal({ children }) {
const router = useRouter();

return (
<div className="modal-overlay" onClick={() => router.back()}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
<button onClick={() => router.back()}>×</button>
{children}
</div>
</div>
);
}

Dashboard with Tabs

app/dashboard/
├── layout.js
├── page.js
├── @analytics/
│ ├── default.js
│ └── page.js
├── @team/
│ ├── default.js
│ └── page.js
└── @activity/
├── default.js
└── page.js
// app/dashboard/layout.js
export default function DashboardLayout({
children,
analytics,
team,
activity,
}) {
return (
<div>
{children}
<div className="grid grid-cols-3 gap-4">
{analytics}
{team}
{activity}
</div>
</div>
);
}

Conditional Slots

// Show different content based on auth
export default function Layout({ children, authRequired, public }) {
const isLoggedIn = checkAuth();

return (
<>
{children}
{isLoggedIn ? authRequired : public}
</>
);
}

Loading and Error States

app/
├── @feed/
│ ├── page.js
│ ├── loading.js # Loading for this slot
│ └── error.js # Error for this slot
└── @sidebar/
├── page.js
└── loading.js # Independent loading

Use Cases

FeatureUse Case
Parallel RoutesDashboards, split views
InterceptingModals, previews, quick views
CombinedInstagram-style photo modals

Key Points

  • @folder creates a parallel route slot
  • (.) prefix intercepts same-level routes
  • Intercepted on soft nav, original on hard nav
  • Each slot can have its own loading/error
  • default.js for unmatched slot states
  • Perfect for modals that work as full pages
  • Combine with useRouter for navigation