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
Modal Component
"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
| Feature | Use Case |
|---|---|
| Parallel Routes | Dashboards, split views |
| Intercepting | Modals, previews, quick views |
| Combined | Instagram-style photo modals |
Key Points
@foldercreates 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.jsfor unmatched slot states- Perfect for modals that work as full pages
- Combine with useRouter for navigation