In the Quantum Metropolis, the advanced transport system allows simultaneous travel along multiple parallel paths, in-flight traffic interception and redirection, and logical route grouping without affecting their addresses. Similarly, Next.js 15 offers advanced routing techniques that enable creating complex user interfaces with elegance and efficiency.
In this chapter, you will learn three powerful routing patterns in the App Router:
Parallel Routes allow simultaneous rendering of multiple pages (or parts of pages) in the same layout. It's like holographic displays in the Quantum Metropolis that can show multiple independent information streams simultaneously.
Parallel Routes use a special naming convention with the
@ prefix:1app/
2├── layout.tsx
3├── page.tsx
4├── @analytics/ # Slot "analytics"
5│ └── page.tsx
6├── @team/ # Slot "team"
7│ └── page.tsx
8└── @activity/ # "activity" Slot
9 └── page.tsxImagine a Quantum Metropolis control panel that simultaneously displays analytics, team activity, and recent events:
1// app/dashboard/layout.tsx
2export default function DashboardLayout({
3 children,
4 analytics,
5 team,
6 activity,
7}: {
8 children: React.ReactNode;
9 analytics: React.ReactNode;
10 team: React.ReactNode;
11 activity: React.ReactNode;
12}) {
13 return (
14 <div className="quantum-dashboard">
15 <div className="main-content">
16 {children}
17 </div>
18
19 <div className="grid grid-cols-3 gap-6 mt-6">
20 <div className="panel">
21 <h2>Quantum Analytics</h2>
22 {analytics}
23 </div>
24
25 <div className="panel">
26 <h2>Team</h2>
27 {team}
28 </div>
29
30 <div className="panel">
31 <h2>Recent Activity</h2>
32 {activity}
33 </div>
34 </div>
35 </div>
36 );
37}1// app/dashboard/@analytics/page.tsx
2async function getAnalytics() {
3 const res = await fetch('https://api.quantum.com/analytics', {
4 next: { revalidate: 60 }
5 });
6 return res.json();
7}
8
9export default async function Analytics() {
10 const data = await getAnalytics();
11
12 return (
13 <div className="analytics-panel">
14 <div className="stat">
15 <span className="label">Quantum Energy</span>
16 <span className="value">{data.energy}%</span>
17 </div>
18 <div className="stat">
19 <span className="label">System Efficiency</span>
20 <span className="value">{data.efficiency}%</span>
21 </div>
22 </div>
23 );
24}1// app/dashboard/@team/page.tsx
2async function getTeam() {
3 const res = await fetch('https://api.quantum.com/team', {
4 next: { revalidate: 300 }
5 });
6 return res.json();
7}
8
9export default async function Team() {
10 const members = await getTeam();
11
12 return (
13 <ul className="team-list">
14 {members.map(member => (
15 <li key={member.id} className="team-member">
16 <img src={member.avatar} alt={member.name} />
17 <span>{member.name}</span>
18 <span className="status">{member.status}</span>
19 </li>
20 ))}
21 </ul>
22 );
23}1// app/dashboard/@activity/page.tsx
2async function getActivity() {
3 const res = await fetch('https://api.quantum.com/activity', {
4 cache: 'no-store' // Always fresh data
5 });
6 return res.json();
7}
8
9export default async function Activity() {
10 const activities = await getActivity();
11
12 return (
13 <div className="activity-feed">
14 {activities.map(activity => (
15 <div key={activity.id} className="activity-item">
16 <span className="timestamp">{activity.time}</span>
17 <p>{activity.description}</p>
18 </div>
19 ))}
20 </div>
21 );
22}When a user navigates in a way that doesn't match the slot structure, Next.js uses the
default.tsx file:1// app/dashboard/@analytics/default.tsx
2export default function DefaultAnalytics() {
3 return <div>No analytics data available</div>;
4}1// app/dashboard/layout.tsx
2export default async function DashboardLayout({
3 children,
4 analytics,
5 team,
6 activity,
7 params,
8}: {
9 children: React.ReactNode;
10 analytics: React.ReactNode;
11 team: React.ReactNode;
12 activity: React.ReactNode;
13 params: Promise<{ userRole: string }>;
14}) {
15 const isAdmin = (await params).userRole === 'admin';
16
17 return (
18 <div className="quantum-dashboard">
19 <div className="main-content">{children}</div>
20
21 <div className="panels">
22 {analytics}
23 {team}
24 {isAdmin && activity} {/* Only for admins */}
25 </div>
26 </div>
27 );
28}Intercepting Routes allow intercepting navigation and displaying an alternative view while the URL changes. It's like quantum tunnels in the Quantum Metropolis - they enable shortcuts without changing the final destination.
Intercepting Routes use special prefixes:
(.) - intercepts segments at the same level(..) - intercepts segments one level up(..)(..) - intercepts segments two levels up(...) - intercepts segments from the app/ rootA classic use case - a photo gallery where clicking opens a modal but the URL changes:
1app/
2├── layout.tsx
3├── page.tsx # Galeria /
4├── photos/
5│ └── [id]/
6│ └── page.tsx # Full photo page /photos/123
7└── @modal/
8 └── (..)photos/
9 └── [id]/
10 └── page.tsx # Intercepting modal /photos/1231// app/page.tsx - Galeria
2import Link from 'next/link';
3import Image from 'next/image';
4
5const photos = [
6 { id: '1', url: '/photos/quantum-city-1.jpg', title: 'Quantum District' },
7 { id: '2', url: '/photos/quantum-city-2.jpg', title: 'Central Plaza' },
8 { id: '3', url: '/photos/quantum-city-3.jpg', title: 'Night Skyline' },
9];
10
11export default function Gallery() {
12 return (
13 <div className="gallery-grid">
14 {photos.map(photo => (
15 <Link key={photo.id} href={`/photos/${photo.id}`}>
16 <div className="photo-card">
17 <Image
18 src={photo.url}
19 alt={photo.title}
20 width={300}
21 height={200}
22 />
23 <p>{photo.title}</p>
24 </div>
25 </Link>
26 ))}
27 </div>
28 );
29}1// app/photos/[id]/page.tsx - Full photo page
2import Image from 'next/image';
3
4async function getPhoto(id: string) {
5 // Fetch photo data
6 return {
7 id,
8 url: `/photos/quantum-city-${id}.jpg`,
9 title: 'Quantum City',
10 description: 'View of Quantum Metropolis',
11 };
12}
13
14export default async function PhotoPage({
15 params,
16}: {
17 params: Promise<{ id: string }>;
18}) {
19 const photo = await getPhoto((await params).id);
20
21 return (
22 <div className="photo-full-page">
23 <h1>{photo.title}</h1>
24 <Image
25 src={photo.url}
26 alt={photo.title}
27 width={1200}
28 height={800}
29 />
30 <p>{photo.description}</p>
31 </div>
32 );
33}1// app/@modal/(.)photos/[id]/page.tsx - Modal
2'use client';
3
4import { useRouter } from 'next/navigation';
5import Image from 'next/image';
6import { useEffect, useState } from 'react';
7
8export default function PhotoModal({
9 params,
10}: {
11 params: { id: string };
12}) {
13 const router = useRouter();
14 const [photo, setPhoto] = useState(null);
15
16 useEffect(() => {
17 // Fetch photo data
18 fetch(`/api/photos/${params.id}`)
19 .then(res => res.json())
20 .then(setPhoto);
21 }, [params.id]);
22
23 if (!photo) return null;
24
25 return (
26 <div
27 className="modal-overlay"
28 onClick={() => router.back()}
29 >
30 <div
31 className="modal-content"
32 onClick={(e) => e.stopPropagation()}
33 >
34 <button
35 className="modal-close"
36 onClick={() => router.back()}
37 >
38 ✕
39 </button>
40
41 <Image
42 src={photo.url}
43 alt={photo.title}
44 width={800}
45 height={600}
46 />
47
48 <h2>{photo.title}</h2>
49 <p>{photo.description}</p>
50 </div>
51 </div>
52 );
53}1// app/layout.tsx - Layout with modal
2export default function RootLayout({
3 children,
4 modal,
5}: {
6 children: React.ReactNode;
7 modal: React.ReactNode;
8}) {
9 return (
10 <html lang="pl">
11 <body>
12 {children}
13 {modal}
14 </body>
15 </html>
16 );
17}1app/
2├── layout.tsx
3├── page.tsx
4├── login/
5│ └── page.tsx # Full login page /login
6└── @auth/
7 └── (.)login/
8 └── page.tsx # Login modal1// app/@auth/(.)login/page.tsx
2'use client';
3
4import { useRouter } from 'next/navigation';
5import { useState } from 'react';
6
7export default function LoginModal() {
8 const router = useRouter();
9 const [email, setEmail] = useState('');
10 const [password, setPassword] = useState('');
11
12 const handleSubmit = async (e: React.FormEvent) => {
13 e.preventDefault();
14 // Login logic
15 await loginUser(email, password);
16 router.back();
17 };
18
19 return (
20 <div className="modal-overlay" onClick={() => router.back()}>
21 <div className="modal-content" onClick={(e) => e.stopPropagation()}>
22 <h2>Log in to Quantum Metropolis</h2>
23
24 <form onSubmit={handleSubmit}>
25 <input
26 type="email"
27 value={email}
28 onChange={(e) => setEmail(e.target.value)}
29 placeholder="Email"
30 required
31 />
32
33 <input
34 type="password"
35 value={password}
36 onChange={(e) => setPassword(e.target.value)}
37 placeholder="Password"
38 required
39 />
40
41 <button type="submit">Log in</button>
42 </form>
43 </div>
44 </div>
45 );
46}Route Groups allow logical grouping of routes without affecting the URL structure. It's like districts in the Quantum Metropolis - they organize the city but don't affect building addresses.
Route Groups use parentheses in folder names:
1app/
2├── (marketing)/ # Group - doesn't affect the URL
3│ ├── layout.tsx # Marketing layout
4│ ├── about/
5│ │ └── page.tsx # URL: /about
6│ └── contact/
7│ └── page.tsx # URL: /contact
8├── (shop)/ # Group - doesn't affect the URL
9│ ├── layout.tsx # Shop layout
10│ ├── products/
11│ │ └── page.tsx # URL: /products
12│ └── cart/
13│ └── page.tsx # URL: /cart
14└── (dashboard)/ # Group - doesn't affect the URL
15 ├── layout.tsx # Dashboard layout
16 ├── analytics/
17 │ └── page.tsx # URL: /analytics
18 └── settings/
19 └── page.tsx # URL: /settings1// app/(marketing)/layout.tsx
2import MarketingHeader from '@/components/MarketingHeader';
3import MarketingFooter from '@/components/MarketingFooter';
4
5export default function MarketingLayout({
6 children,
7}: {
8 children: React.ReactNode;
9}) {
10 return (
11 <div className="marketing-layout">
12 <MarketingHeader />
13 <main className="marketing-content">
14 {children}
15 </main>
16 <MarketingFooter />
17 </div>
18 );
19}1// app/(dashboard)/layout.tsx
2import DashboardSidebar from '@/components/DashboardSidebar';
3import DashboardHeader from '@/components/DashboardHeader';
4
5export default function DashboardLayout({
6 children,
7}: {
8 children: React.ReactNode;
9}) {
10 return (
11 <div className="dashboard-layout">
12 <DashboardHeader />
13 <div className="dashboard-container">
14 <DashboardSidebar />
15 <main className="dashboard-content">
16 {children}
17 </main>
18 </div>
19 </div>
20 );
21}1app/
2├── (auth)/
3│ ├── login/
4│ │ └── page.tsx # /login
5│ ├── register/
6│ │ └── page.tsx # /register
7│ └── forgot-password/
8│ └── page.tsx # /forgot-password
9├── (public)/
10│ ├── about/
11│ │ └── page.tsx # /about
12│ └── blog/
13│ └── [slug]/
14│ └── page.tsx # /blog/:slug
15└── (protected)/
16 ├── middleware.ts # Middleware only for this group
17 ├── dashboard/
18 │ └── page.tsx # /dashboard
19 └── profile/
20 └── page.tsx # /profileRoute Groups allow creating multiple root layouts in the same application:
1app/
2├── (shop)/
3│ ├── layout.tsx # Root layout for the shop
4│ └── products/
5│ └── page.tsx
6└── (blog)/
7 ├── layout.tsx # Root layout for the blog
8 └── posts/
9 └── page.tsx1// app/(shop)/layout.tsx
2export default function ShopRootLayout({
3 children,
4}: {
5 children: React.ReactNode;
6}) {
7 return (
8 <html lang="pl">
9 <body className="shop-theme">
10 {children}
11 </body>
12 </html>
13 );
14}1// app/(blog)/layout.tsx
2export default function BlogRootLayout({
3 children,
4}: {
5 children: React.ReactNode;
6}) {
7 return (
8 <html lang="pl">
9 <body className="blog-theme">
10 {children}
11 </body>
12 </html>
13 );
14}The true power of the App Router reveals itself when you combine all three techniques together:
1app/
2├── (marketing)/
3│ ├── layout.tsx
4│ ├── page.tsx
5│ └── @modal/
6│ └── (.)contact/
7│ └── page.tsx
8├── (dashboard)/
9│ ├── layout.tsx
10│ ├── @analytics/
11│ │ └── page.tsx
12│ ├── @team/
13│ │ └── page.tsx
14│ └── @activity/
15│ └── page.tsx
16└── (shop)/
17 ├── layout.tsx
18 ├── products/
19 │ └── [id]/
20 │ └── page.tsx
21 └── @cart/
22 └── (.)products/
23 └── [id]/
24 └── page.tsx1// app/(shop)/layout.tsx
2export default function ShopLayout({
3 children,
4 cart,
5}: {
6 children: React.ReactNode;
7 cart: React.ReactNode;
8}) {
9 return (
10 <div className="shop-layout">
11 <ShopHeader />
12 <main>{children}</main>
13 {cart}
14 <ShopFooter />
15 </div>
16 );
17}1// app/(shop)/products/[id]/page.tsx
2async function getProduct(id: string) {
3 const res = await fetch(`https://api.quantum.com/products/${id}`);
4 return res.json();
5}
6
7export default async function ProductPage({
8 params,
9}: {
10 params: Promise<{ id: string }>;
11}) {
12 const product = await getProduct((await params).id);
13
14 return (
15 <div className="product-page">
16 <h1>{product.name}</h1>
17 <p>{product.description}</p>
18 <button>Add to cart</button>
19 </div>
20 );
21}1// app/(shop)/@cart/(.)products/[id]/page.tsx
2'use client';
3
4import { useRouter } from 'next/navigation';
5
6export default function QuickViewModal({
7 params,
8}: {
9 params: { id: string };
10}) {
11 const router = useRouter();
12
13 return (
14 <div className="modal-overlay" onClick={() => router.back()}>
15 <div className="quick-view" onClick={(e) => e.stopPropagation()}>
16 <ProductQuickView productId={params.id} />
17 <button onClick={() => router.back()}>Close</button>
18 </div>
19 </div>
20 );
21}1// ✅ Good - independent panels
2app/dashboard/
3├── @analytics/
4├── @team/
5└── @notifications/
6
7// ❌ Bad - dependent components
8app/dashboard/
9├── @header/ // This should be a component, not a slot
10├── @footer/ // This should be a component, not a slot
11└── @content/1// ✅ Good
2app/
3├── photos/[id]/page.tsx
4└── @modal/(.)photos/[id]/page.tsx
5
6// ❌ Bad - modal as a separate route
7app/
8├── photos/[id]/page.tsx
9└── modal/photos/[id]/page.tsx1// ✅ Good - logical grouping
2app/
3├── (marketing)/
4├── (dashboard)/
5└── (auth)/
6
7// ❌ Bad - grouping by file type
8app/
9├── (pages)/
10├── (components)/
11└── (api)/Advanced routing techniques in Next.js 15 are like the advanced transport network of the Quantum Metropolis - they enable creating complex, efficient, and elegant user interfaces:
These techniques give you incredible control over the application structure and user experience. Just as engineers of the Quantum Metropolis design multi-level transport systems for maximum efficiency, you too can design advanced routing structures for optimal UX.
The key to success is understanding when to use each technique and how to combine them to create a cohesive, efficient Next.js 15 application.