Intercepting Route — (.) (..) — modal pattern
Muammodan boshlaylik
Instagram da foto lentasini ko'ryapsiz. Bitta fotoga bosasiz — modal ochiladi, URL /p/abc123 ga o'zgaradi. Orqaga bosdingiz — lenta qaytadi, modal yopiladi.
Lekin /p/abc123 URL ini to'g'ridan-to'g'ri brauzerga yozsangiz — to'liq foto sahifasi ochiladi, lenta yo'q.
Bir xil URL — ikki xil ko'rinish:
- Lentadan bosilganda → modal
- To'g'ridan-to'g'ri kirilganda → to'liq sahifa
Bu — Intercepting Route.
Qanday ishlaydi
Intercepting Route — bir route boshqa route ni "to'xtatib" o'z ichida ko'rsatadi.
Konventsiya:
(.) → bir xil darajadagi route ni to'xtatish
(..) → bir daraja yuqoridagi route ni to'xtatish
(..)(..) → ikki daraja yuqori
(...) → root dan
Amaliy misol — foto modal
Instagram kabi: foto ro'yxatidan bosib modal, URL dan kirsa to'liq sahifa.
app/
├── photos/
│ ├── page.tsx → /photos (ro'yxat)
│ ├── [id]/
│ │ └── page.tsx → /photos/42 (to'liq sahifa)
│ └── (.)photos/ ← intercepting!
│ └── [id]/
│ └── page.tsx → /photos/42 (modal sifatida)
(.)photos — "bir xil darajadagi photos route ni to'xtat" degani.
To'liq implementatsiya
// app/photos/page.tsx — foto ro'yxati
import Link from 'next/link'
export default async function PhotosPage() {
const photos = await getPhotos()
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '8px' }}>
{photos.map(photo => (
<Link key={photo.id} href={`/photos/${photo.id}`}>
<img src={photo.thumbnail} alt={photo.title} />
</Link>
))}
</div>
)
}
// app/photos/[id]/page.tsx — to'liq sahifa (URL dan to'g'ridan-to'g'ri kirilganda)
export default async function PhotoPage({ params }) {
const { id } = await params
const photo = await getPhoto(id)
return (
<div>
<img src={photo.url} alt={photo.title} />
<h1>{photo.title}</h1>
<p>{photo.description}</p>
</div>
)
}
// app/photos/(.)photos/[id]/page.tsx — modal (ro'yxatdan bosilganda)
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
export default function PhotoModal({ params }) {
const router = useRouter()
const { id } = params
// ESC bosilganda yopish
useEffect(() => {
function handleKey(e: KeyboardEvent) {
if (e.key === 'Escape') router.back()
}
window.addEventListener('keydown', handleKey)
return () => window.removeEventListener('keydown', handleKey)
}, [router])
return (
<div
style={{
position: 'fixed', inset: 0,
background: 'rgba(0,0,0,0.8)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
zIndex: 50,
}}
onClick={() => router.back()} // tashqariga bosib yopish
>
<div onClick={e => e.stopPropagation()}>
<PhotoContent id={id} />
</div>
</div>
)
}
Parallel Route bilan birga — to'liq modal pattern
Intercepting Route ko'pincha Parallel Route bilan birga ishlatiladi. Modal layout da @modal slot sifatida ko'rsatiladi:
app/
├── layout.tsx ← @modal slot qabul qiladi
├── photos/
│ ├── page.tsx
│ └── [id]/
│ └── page.tsx
└── @modal/
└── (.)photos/
└── [id]/
└── page.tsx ← modal
// app/layout.tsx
export default function RootLayout({
children,
modal,
}: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
<html>
<body>
{children}
{modal} {/* modal shu yerda ko'rsatiladi */}
</body>
</html>
)
}
// app/@modal/default.tsx — modal yo'q bo'lganda
export default function ModalDefault() {
return null
}
Endi:
/photosga kirganda —children= foto ro'yxati,modal= null- Fotoga bosilganda — URL
/photos/42,children= ro'yxat (o'zgarmaydi!),modal= modal - To'g'ridan-to'g'ri
/photos/42ga kirilganda —children= to'liq sahifa,modal= null
(.) konventsiyasi — qaysi daraja
app/
├── feed/
│ └── page.tsx
└── photos/
├── page.tsx
├── [id]/
│ └── page.tsx
└── (.)photos/ ← photos/ bilan bir xil daraja → (.)
└── [id]/
└── page.tsx
Agar intercepting route bir daraja yuqorida bo'lsa:
app/
├── photos/
│ └── [id]/
│ └── page.tsx
└── feed/
└── (..)photos/ ← bir daraja yuqori → (..)
└── [id]/
└── page.tsx
Tez-tez uchraydigan xatolar
1. default.tsx yo'q — navigatsiyada xato
// ❌ — @modal slot uchun default.tsx yo'q
// Boshqa sahifaga o'tganda modal slot 404
// ✅
app/@modal/default.tsx → null qaytaradi
2. Daraja konventsiyasini noto'g'ri tanlash
// app/photos/(.)photos/[id] — to'g'ri (bir xil daraja)
// app/photos/(..)photos/[id] — xato (bir daraja yuqori emas)
Papkalar joylashuviga qarab (.), (..), (...) tanlanadi.
3. Modal ichida router.back() o'rniga router.push() ishlatish
// ❌ — modal yopilganda ro'yxat yo'qoladi
onClick={() => router.push('/photos')}
// ✅ — orqaga qaytish, ro'yxat saqlanadi
onClick={() => router.back()}
Xulosa
Intercepting Route — bir xil URL, ikki xil ko'rinish.
Ro'yxatdan bosilganda → modal (intercepting route)
URL dan to'g'ridan-to'g'ri → to'liq sahifa
(.) → bir xil daraja
(..) → bir daraja yuqori
(...) → root dan
Parallel Route (@modal) + Intercepting Route → to'liq modal pattern
Keyingi dars
28-dars: Middleware — request kelganda ishlaydigan kod, matcher
Har bir so'rov kelganda — autentifikatsiya tekshiruvi, redirect, header qo'shish — middleware bilan.