IlmHamroh
JavaScript Full-stack/23-qism. Next.js8/43-dars18 daqiqa
Mundarija (28)

Next.js parallel va intercepting routes: @slot, default.tsx va URL'li modal

Qisqacha: Parallel routes — bitta layout'da bir nechta mustaqil "oyna": @modal kabi @ bilan boshlangan papka slot bo'ladi va layout'ga children bilan yonma-yon prop bo'lib keladi. Ushlovchi marshrut (intercepting route) — (.) kabi belgili papka: ilova ichidan biror manzilga o'tilganda o'sha sahifa o'rniga boshqasini (masalan modal) ko'rsatadi, URL esa haqiqiy manzilga o'zgaradi. Ikkalasi birga URL'li modal beradi: bosilsa — sahifa ustida oyna, havolani ulashsa yoki yangilasa — to'liq sahifa. Slot mos sahifa topmaganda default.tsx ishlaydi; Next 16.4 da u bo'lmasa build to'xtaydi.

Bu darsda

  • @slot papka yaratasiz va uni layout'da children yonida chizasiz.
  • default.tsx nega kerakligini soft va hard navigatsiya farqi bilan tushuntirasiz.
  • (.), (..), (...) belgilarini o'qiy olasiz va qaysi manzilni ushlashini aytasiz.
  • portfolio da loyiha rasmini URL'li modal oynada ochasiz — native <dialog> bilan.
  • Next 16.4 ning ikki qat'iy build tekshiruvini haqiqiy xabar bilan ko'rasiz.

Oldin bilishingiz kerak: Route groups, Layout va template, Modal, drawer, popover, Navigatsiya.

1. Nega bu kerak?

Case study sahifasida loyihaning skrinshoti bor. Uni kattalashtirib ko'rish uchun ikki odatiy yo'l bor, ikkalasi ham chala.

Birinchi yo'l — oddiy modal. Modal, drawer, popover darsidagidek: tugma bosiladi, useState bilan oyna ochiladi. Qulay, lekin URL o'zgarmaydi. Rasmni do'stingizga yubormoqchisiz — havola faqat sahifani ochadi, rasm yopiq. Telefonda «Orqaga» bosilsa — oyna yopilmaydi, butun sahifadan chiqib ketasiz.

Ikkinchi yo'l — alohida sahifa. /loyihalar/vazifalar/rasm — o'z URL'i bor, ulashsa bo'ladi. Lekin bosilganda case study yo'qoladi, foydalanuvchi kontekstdan uziladi.

Bizga ikkalasining yaxshi tomoni kerak: ilova ichida bosilganda — modal, case study orqada turadi; URL esa /loyihalar/vazifalar/rasm bo'ladi. Havolani ochgan yoki sahifani yangilagan odam — to'liq sahifani ko'radi. «Orqaga» — oynani yopadi.

Instagram'ni eslang: lentada rasm bosilsa, u lenta ustida ochiladi, manzil satri esa /p/… ga o'zgaradi. Shu havolani yangi tabda ochsangiz — rasm alohida sahifada. Next.js'da bu naqsh ikki xususiyatning birikmasi: parallel routes va ushlovchi marshrutlar.

2. Parallel routes: @slot

2.1 Slot — layout'ning qo'shimcha "teshigi"

Layout va template darsida layout bitta "teshik" olardi — children. Parallel routes bilan teshiklar ko'payadi. Papka nomi @ bilan boshlansa, u slot (uya) bo'ladi va ota layout'ga o'z nomi bilan prop bo'lib keladi.

Klassik misol — boshqaruv paneli (dashboard), ikki mustaqil blok:

text
app/panel/
├── @analitika/page.tsx
├── @jamoa/page.tsx
├── layout.tsx
└── page.tsx
tsx
export default function PanelLayout({
  children,
  analitika,
  jamoa,
}: LayoutProps<"/panel">) {
  return (
    <>
      {children}
      <section>{analitika}</section>
      <section>{jamoa}</section>
    </>
  );
}

/panel ochilganda Next.js uchta sahifani bir vaqtda chizadi: page.tsx → children, @analitika/page.tsx → analitika, @jamoa/page.tsx → jamoa. LayoutProps<"/panel"> slot nomlarini o'zi biladi — typegen papkalarni o'qiydi.

Uch muhim qoida:

  • Slot — URL segmenti emas. @analitika URL'ga kirmaydi: /panel/@analitika degan manzil yo'q. Bu jihatdan u route group ga o'xshaydi.
  • children ham slot, faqat yashirin: app/panel/page.tsx aslida app/panel/@children/page.tsx bilan bir xil.
  • Har slot mustaqil. Har biriga o'z loading.tsx va error.tsx ni qo'yish mumkin: analitika sekin bo'lsa, jamoa bloki uni kutmaydi; analitika yiqilsa, jamoa bloki ishlayveradi.

2.2 Soft va hard navigatsiya

Endi /panel/sozlamalar sahifasini qo'shsak — app/panel/sozlamalar/page.tsx. Bu manzilda @analitika va @jamoa nima ko'rsatadi? Ularda sozlamalar/page.tsx yo'q-ku.

Next.js ikki holatni ajratadi:

Navigatsiya Qanday Mos kelmagan slot
Soft (yumshoq) <Link> bilan, ilova ichida Oldingi holatini saqlaydi
Hard (qattiq) Manzil satri, yangilash, yangi tab default.tsx ni chizadi

Soft navigatsiyada Next.js qisman render qiladi: faqat o'zgargan slot yangilanadi, qolganlari avvalgi sahifasida qoladi — URL'ga mos kelmasa ham. Hard navigatsiyada esa brauzer hamma narsani noldan so'raydi, Next.js "bu slotda oldin nima edi" degan xotiraga ega emas. Shuning uchun unga zaxira kerak.

2.3 default.tsx — slotning zaxirasi

default.tsx — slot uchun mos sahifa topilmaganda chiziladigan fayl. Kanondagi @modal/default.tsx:

tsx
// Slot uchun mos sahifa bo'lmasa (to'g'ridan kirish, yangilash)
// — hech narsa ko'rsatilmaydi (N-08).
export default function ModalYoq() {
  return null;
}

null — "hech narsa chizma". Modal slot uchun aynan shu kerak: modal faqat ilova ichidan ochilganda bor, boshqa paytda — yo'q.

2.4 Biz topdik: Next 16.4 da default.tsx siz build to'xtaydi

Next.js hujjatining o'zi bu yerda ikki xil gapiradi (16.4.0 paketidagi hujjat, 2026-10-21):

Hujjat sahifasi Nima deydi
Parallel Routes default.tsx bo'lmasa, hard navigatsiyada 404 chiziladi
default.js nomli slotda default.tsx bo'lmasa — xato, faylni yozish shart
16-versiyaga o'tish qo'llanmasi hamma slotga default.js shart, bo'lmasa build yiqiladi

Birinchi sahifa eski xulqni tasvirlaydi, qolgan ikkitasi — Next 16 nikini. Biz holat 08 dan @modal/default.tsx ni olib tashlab sinadik — build umuman o'tmadi (asosiy qatorlar):

text
⚠ Strict route matching is enabled by default. This validation indicates a bug in your app's route structure, but you can temporarily restore loose route matching by setting `deprecated.looseRouteMatching` to `true` in your Next.js config.
Error: Parallel route slots cannot render the same URLs
The following layouts have parallel route slots that cannot render the same URLs:
app/(sayt)/loyihalar/layout.tsx
- /loyihalar is missing a matching page or default.tsx in @modal
- /loyihalar/[slug] is missing a matching page or default.tsx in @modal
- /loyihalar/[slug]/rasm is missing a matching page or default.tsx in @modal
Every URL matched by one slot must have a matching page or default.tsx in every sibling slot.

Tarjimasi: "Qat'iy marshrut moslashi sukut bo'yicha yoqilgan. … Parallel slotlar bir xil URL'larni chiza olmaydi: /loyihalar uchun @modal da mos sahifa yoki default.tsx yo'q" — va shunday uchta manzil. Oxirgi qator qoidani aytadi: "bitta slot mos keladigan har URL uchun qo'shni har slotda mos sahifa yoki default.tsx bo'lishi shart".

Kuzatganimiz o'tish qo'llanmasiga mos: Next 16 xatoni ish vaqtidagi 404 dan build vaqtiga ko'chirgan. Hujjat bilan amaliyot farq qilsa, ishonadiganimiz — haqiqiy build natijasi. Yaxshi tomoni — xato foydalanuvchiga yetmaydi. Ogohlantirishdagi deprecated.looseRouteMatching — eski (bo'sh) xulqni vaqtincha qaytarish yo'li; nomidagi deprecated "bu yo'l yopilmoqda" degani. Xatoda yana ikki qator bor edi (birinchi xatoning oqibati) — ularni keyingi bo'limda alohida ko'ramiz.

2.5 Slot ichida o'z navigatsiyasi: tablar

Slot ichiga ham layout.tsx va bir nechta sahifa qo'yish mumkin. Masalan @jamoa/page.tsx va @jamoa/arxiv/page.tsx, ustida @jamoa/layout.tsx da ikki havola — «Faol» va «Arxiv». Foydalanuvchi /panel/arxiv ga o'tganda faqat jamoa bloki almashadi, analitika esa joyida qoladi (soft navigatsiya qoidasi). Hujjat buni tablar uchun tavsiya qiladi: har tab — haqiqiy URL, ulashsa bo'ladi.

Bu kuchli, lekin chalkash ham. Har yangi URL uchun har slotda mos sahifa yoki default.tsx kerak — aks holda yuqoridagi «Next 16.4 da default.tsx siz build to'xtaydi» bo'limidagi xato. Slotlar soni ko'paygani sari fayllar soni tez o'sadi. Shuning uchun kichik loyihada parallel routes'ni faqat haqiqiy ehtiyojda (modal, mustaqil panel) ishlatish ma'qul — portfolio da ham bitta slot, faqat modal uchun.

Tekshirib ko'ring: /panel/sozlamalar ga <Link> bilan o'tildi. @analitika da sozlamalar yo'q. Ekranda nima bo'ladi? Endi sahifani yangilang-chi?

Javob

<Link> bilan (soft) — @analitika oldingi holatini (/panel dagi analitikani) ko'rsatishda davom etadi. Yangilanganda (hard) — @analitika/default.tsx chiziladi. U bo'lmasa, Next 16.4 build'ning o'zi o'tmagan bo'lardi.

3. Ushlovchi marshrutlar: (.)

3.1 "Ushlab qolish" g'oyasi

Ushlovchi marshrut (intercepting route) — papka nomining boshidagi maxsus belgi: "ilova ichidan shu manzilga o'tilganda, haqiqiy sahifa o'rniga meni ko'rsat". Belgilar fayl tizimidagi ./ va ../ ga o'xshaydi:

Belgi Qaysi manzilni ushlaydi
(.)rasm Shu darajadagi rasm segmentini
(..)rasm Bir daraja yuqoridagi
(..)(..)rasm Ikki daraja yuqoridagi
(...)rasm app ildizidan boshlab

Muhim: darajalar URL segmentlari bo'yicha sanaladi, fayl tizimi bo'yicha emas. @modal slot va (sayt) guruh segment emas — ular sanalmaydi.

3.2 Kanondagi tuzilma

text
app/(sayt)/loyihalar/
├── @modal/
│   ├── (.)[slug]/rasm/page.tsx   ← modal (ushlaydi)
│   └── default.tsx               ← null
├── [slug]/
│   ├── rasm/page.tsx             ← to'liq sahifa
│   └── page.tsx                  ← case study
├── layout.tsx                    ← {children}{modal}
└── page.tsx

@modal/(.)[slug]/rasm ni o'qiymiz. @modal — slot, sanalmaydi. Demak (.) "loyihalar darajasida" degani: u /loyihalar/[slug]/rasm ni ushlaydi. Fayl tizimida u ikki papka chuqurroq turgan bo'lsa ham.

loyihalar/layout.tsx — ikki slotni yonma-yon qo'yadi:

tsx
// Parallel route: `modal` slot (@modal papkasi) children bilan
// yonma-yon chiziladi (N-08).
export default function LoyihalarLayout({
  children,
  modal,
}: LayoutProps<"/loyihalar">) {
  return (
    <>
      {children}
      {modal}
    </>
  );
}

Endi ikki ssenariy:

flowchart TB
  A["/loyihalar/vazifalar da<br/>rasm bosildi (soft)"] --> B["children: case study qoladi<br/>modal: (.)[slug]/rasm"]
  C["/loyihalar/vazifalar/rasm<br/>to'g'ridan yoki yangilash (hard)"] --> D["children: [slug]/rasm — to'liq sahifa<br/>modal: default.tsx → null"]

Soft navigatsiyada children slot o'zgarmaydi (case study joyida — oldingi bo'limdagi qoida), modal slot esa ushlovchi sahifani oladi. Hard navigatsiyada ushlash yo'q: children haqiqiy [slug]/rasm/page.tsx ni, modal esa default.tsx ni chizadi.

3.3 Biz topdik: ushlovchi marshrutga "asl" sahifa shart

To'liq sahifa ([slug]/rasm/page.tsx) ni olib tashlab, faqat modalni qoldirsak-chi? Build:

text
Error: Interception routes must have a canonical route
The following interception routes do not have a canonical route:
- /loyihalar/(.)[slug]/rasm (expected /loyihalar/[slug]/rasm)
Every interception route must have a matching non-interception route so the URL can be loaded directly or refreshed.

Tarjimasi: "Ushlovchi marshrutning asl (canonical) marshruti bo'lishi shart. … Har ushlovchi marshrut uchun oddiy marshrut kerak — URL'ni to'g'ridan ochish yoki yangilash mumkin bo'lsin". Bu ham 16.4 ning qat'iy tekshiruvi. Mantiqi oddiy: modal havolasini kimdir ulashsa, uni ochadigan sahifa bo'lishi kerak. Avvalgi bo'limdagi default.tsx siz build'da ham shu xato chiqqan edi — slot xatosi sabab asl sahifa "topilmay" qolgandi.

Tekshirib ko'ring: fayl app/(sayt)/blog/@modal/(..)loyihalar/[slug]/page.tsx. U qaysi manzilni ushlaydi?

Javob

/loyihalar/[slug]. (sayt) va @modal sanalmaydi; blog — bitta segment. (..) undan bir daraja yuqoriga (ildizga) chiqadi va loyihalar/[slug] ni ushlaydi. Ya'ni blogdan loyiha havolasi bosilsa — loyiha modal'da ochiladi.

4. Modal oynaning o'zi

4.1 RasmOynasi — native <dialog>

Ushlovchi sahifa rasmni RasmOynasi ga o'raydi. U — client komponent:

tsx
"use client";

import { useRouter } from "next/navigation";
import { useEffect, useId, useRef } from "react";

// Native <dialog>: Esc, fokus tuzog'i, fokus qaytishi —
// brauzerniki (20-qism qarori). Yopish = tarixda orqaga
// (URL ham qaytadi).
export function RasmOynasi({
  sarlavha,
  children,
}: {
  sarlavha: string;
  children: React.ReactNode;
}) {
  const router = useRouter();
  const oyna = useRef<HTMLDialogElement>(null);
  const sarlavhaId = useId();

  useEffect(() => {
    const d = oyna.current;
    if (d && !d.open) d.showModal();
  }, []);

  return (
    <dialog
      ref={oyna}
      className="rasm-oynasi"
      aria-labelledby={sarlavhaId}
      onClose={() => router.back()}
    >
      <h2 id={sarlavhaId}>{sarlavha}</h2>
      {children}
      <form method="dialog">
        <button type="submit">Yopish</button>
      </form>
    </dialog>
  );
}

Kanon izohi telefonga sig'ishi uchun uch qatorga bo'lindi. Asosiy qarorlar:

  • Native <dialog> + showModal() — Esc bilan yopish, fokusni oyna ichida ushlash va yopilganda qaytarish brauzerning o'zida. 20-qismda kanon shu yo'lni tanlagan edi (Radix Dialog kanon CSP'si bilan to'qnashardi). showModal() effektda: element DOM'da bo'lgandan keyingina chaqirish mumkin.
  • <form method="dialog"> — JS'siz yopish tugmasi: bunday forma yuborilganda brauzer dialogni yopadi.
  • onClose={() => router.back()} — oyna qanday yopilmasin (Esc, «Yopish»), URL ham orqaga qaytadi. Modal URL'ning qismi — yopish ham URL orqali.

4.2 Kanon oqim tekshiruvi

Kanon modal oqimini headless Chrome'da tekshirdi (xom natija):

text
modal: /loyihalar/vazifalar/rasm | dialog nomi: Vazifalar ilovasi — skrinshot | h1: Vazifalar ilovasi
fokus dialog ichidami: true
Esc dan keyin: /loyihalar/vazifalar | dialog soni: 0
Yopish dan keyin: /loyihalar/vazifalar
to'g'ridan kirish h1: Vazifalar ilovasi — skrinshot | dialog soni: 0
yangilash h1: Vazifalar ilovasi — skrinshot

Qatorma-qator:

Qadam URL Ekranda
Rasm bosildi /loyihalar/vazifalar/rasm Dialog ochiq, orqada case study (h1 o'zgarmagan)
Esc yoki «Yopish» /loyihalar/vazifalar Dialog yo'q
To'g'ridan kirish / yangilash /loyihalar/vazifalar/rasm To'liq sahifa, h1 "… — skrinshot", dialog yo'q

Bir manzil — ikki xil ko'rinish, va ikkalasi ham to'g'ri.

4.3 Natija oynasida: URL'li modal modeli

Natija oynasida fayl-routing yo'q, lekin xulqni o'quv modeli bilan qayta qurish mumkin. Ushlashni oddiy bayroq bilan taqlid qilamiz: <Link> ning onNavigate i "ilova ichidan o'tdik" deb belgilaydi. Bu Next.js'ning haqiqiy mexanizmi emas — faqat g'oyani ko'rish uchun.

tsx
import Link from "next/link";
import { ih, usePathname, useRouter } from "next/navigation";

ih.boshla("/loyihalar/vazifalar");

function CaseStudy() {
  return (
    <>
      <h1>Vazifalar ilovasi</h1>
      <Link href="/loyihalar/vazifalar/rasm" onNavigate={onSoft}>
        Rasmni kattalashtirish
      </Link>
    </>
  );
}

let soft = false;
function onSoft() {
  soft = true; // Next.js: ilova ichidan o'tish — ushlash ishlaydi
}

function Modal() {
  const router = useRouter();
  return (
    <div role="dialog" aria-label="skrinshot">
      <p>[rasm] skrinshot</p>
      <button onClick={() => router.back()}>Yopish</button>
    </div>
  );
}

export default function App() {
  const path = usePathname();
  const isPhoto = path.endsWith("/rasm");
  console.log(path, isPhoto ? (soft ? "modal" : "to'liq") : "sahifa");
  if (isPhoto && !soft) return <h1>Vazifalar ilovasi — skrinshot</h1>;
  return (
    <>
      <CaseStudy />
      {isPhoto && <Modal />}
    </>
  );
}

Konsolda:

text
/loyihalar/vazifalar sahifa
/loyihalar/vazifalar/rasm modal
/loyihalar/vazifalar sahifa

Ekranda:

text
Vazifalar ilovasi
Rasmni kattalashtirish

Havola bosilganda URL /rasm ga o'zgardi, lekin case study qoldi va ustida "oyna" chiqdi (ekran("skrinshot") tekshiradi). «Yopish» — router.back(), URL qaytdi, oyna yo'qoldi. Kanon oqimining birinchi uch qatori aynan shunday.

Hard navigatsiyani ko'rish uchun blok boshidagi qatorni ih.boshla("/loyihalar/vazifalar/rasm") ga o'zgartiring: soft hali false, ekranda faqat "Vazifalar ilovasi — skrinshot" — to'liq sahifa.

5. Portfolio qadami: rasm modal'da va commit

5.1 Fayllar

Route groups darsidagi o'zgarishlarga qo'shiladi:

Fayl Nima
loyihalar/layout.tsx {children}{modal}
loyihalar/@modal/default.tsx null
loyihalar/@modal/(.)[slug]/rasm/page.tsx Ushlovchi: RasmOynasi + rasm
loyihalar/[slug]/rasm/page.tsx Asl: to'liq sahifa, ← Loyiha havolasi
loyihalar/_komponentlar/RasmOynasi.tsx Client: <dialog>, router.back()
loyihalar/[slug]/page.tsx Case study'da rasm-havola
public/loyihalar/*.png 1280×800 namuna rasmlar (o'zingiznikini qo'ying)

Rasm next/image bilan chiziladi — u next/image darsida ochiladi, hozircha oddiy <img> deb tushuning. Ikkala rasm sahifasida ham generateStaticParams bor: aks holda Fayl-asosli routing darsidagi "uncached or runtime data" build xatosi qaytadi.

5.2 Build va o'lchov

Build jadvalida yangi qatorlar (kanon, holat 08):

text
├   /loyihalar/(.)[slug]/rasm
│ ├ ◐ /loyihalar/(.)[slug]/rasm
│ ├ ○ /loyihalar/(.)vazifalar/rasm
│ ├ ○ /loyihalar/(.)bahor/rasm
│ └ ○ /loyihalar/(.)landing/rasm
├   /loyihalar/[slug]
│ ├ ◐ /loyihalar/[slug]
│ ├ ○ /loyihalar/vazifalar
│ ├ ○ /loyihalar/bahor
│ └ ○ /loyihalar/landing
└   /loyihalar/[slug]/rasm
  ├ ◐ /loyihalar/[slug]/rasm
  ├ ○ /loyihalar/vazifalar/rasm
  ├ ○ /loyihalar/bahor/rasm
  └ ○ /loyihalar/landing/rasm

Ushlovchi marshrut ham alohida, (.) bilan, oldindan tayyorlangan (○). Bu URL emas — Next.js'ning ichki nomi. Foydalanuvchi uni hech qachon ko'rmaydi.

Sahifa JS (gzip)
/loyihalar/vazifalar 149.1 KB (+5.7: next/image + modal)
/loyihalar/vazifalar/rasm 143.4 KB
Boshqa sahifalar 137.9–143.4 KB

axe 0 (6 sahifa), hydration 0; lint, tip, format — 0; build 22 s.

bash
git add -A
git commit

Commit (kanon): Tuzilma: (sayt) guruhi, _komponentlar; loyiha rasmi modal'da (@modal + (.)[slug]/rasm).

6. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Parallel route slots cannot render the same URLs Slotda default.tsx yo'q @slot/default.tsx (return null)
Interception routes must have a canonical route Faqat modal bor, asl sahifa yo'q [slug]/rasm/page.tsx ni yozing
Modal yangilanganda ham ochiq qoladi (eski versiyalar) default.tsx o'rnida noto'g'ri sahifa default.tsx — null
Modal ochilmaydi, to'liq sahifa chiqadi (..) darajasi noto'g'ri sanalgan Slot va guruhni sanamang
Yopilganda URL o'zgarmaydi Faqat dialog.close() onClose da router.back()
showModal xatosi Element DOM'ga tushmasdan chaqirilgan useEffect ichida
Slot prop'i undefined Slot papka nomi xato (@Modal / modal) @ + layout'dagi prop nomi bir xil

7. Mashqlar

Mashqlar kurs/mashqlar/23/08-parallel-intercepting/ da.

1-mashq (oson): belgilarni o'qing

  • app/lenta/(..)rasm/[id]/page.tsx — ushlaydi: ]
  • app/lenta/@modal/(.)post/[id]/page.tsx — ushlaydi: ]
  • app/a/b/(...)kirish/page.tsx — ushlaydi:
  • app/panel/@jamoa/page.tsx — URL:
Yechim
  • /rasm/[id] — lenta dan bir daraja yuqori — ildiz.
  • /lenta/post/[id] — @modal slot sanalmaydi, (.) — lenta darajasi.
  • /kirish — (...) doim ildizdan.
  • /panel — slot URL'ga kirmaydi; u /panel sahifasining bir qismi.

2-mashq (o'rta): qaysi manzilni ushlaydi?

Funksiya yozing: interceptedUrl(folder) — ushlovchi papka yo'lini oladi ("app/(sayt)/loyihalar/@modal/(.)[slug]/rasm") va u ushlaydigan URL'ni qaytaradi ("/loyihalar/[slug]/rasm"). Qoidalar: app tashlanadi; (guruh) va @slot sanalmaydi; ushlovchi belgigacha bo'lgan segmentlar — "joriy daraja"; (.) — shu daraja, har (..) — bitta yuqori, (...) — ildiz; belgidan keyingi qism qo'shiladi. Ishora: RegExp bilan ^(\(\.\.\.\)|(\(\.\.\))+|\(\.\)) ni ajrating.

Yechim
js
const MARK = /^(\(\.\.\.\)|(?:\(\.\.\))+|\(\.\))/;

function interceptedUrl(folder) {
  const parts = folder.split("/").slice(1);
  const level = [];
  for (let i = 0; i < parts.length; i++) {
    const part = parts[i];
    const m = part.match(MARK);
    if (m) {
      let base = level;
      if (m[1] === "(...)") base = [];
      else if (m[1] !== "(.)") {
        base = level.slice(0, -(m[1].length / 4));
      }
      const rest = [part.slice(m[1].length), ...parts.slice(i + 1)];
      return "/" + [...base, ...rest].join("/");
    }
    const isGroup = part.startsWith("(") && part.endsWith(")");
    if (!isGroup && !part.startsWith("@")) level.push(part);
  }
  return null;
}

const folders = [
  "app/(sayt)/loyihalar/@modal/(.)[slug]/rasm",
  "app/lenta/(..)rasm/[id]",
  "app/a/b/(..)(..)kirish",
  "app/a/b/(...)kirish",
];
for (const f of folders) console.log(interceptedUrl(f));

Konsolda:

text
/loyihalar/[slug]/rasm
/rasm/[id]
/kirish
/kirish

(..) 4 belgidan iborat — m[1].length / 4 nechta daraja yuqoriga chiqishni beradi. (...) alohida tekshiriladi: u ham (.. bilan boshlanadi, shuning uchun regex'da birinchi turibdi. Slot va guruh level ga qo'shilmadi — hujjatdagi "URL segmentlari bo'yicha" qoidasi.

3-mashq (qiyin): galereya — modal ichida keyingi rasm

«URL'li modal modeli» blokini galereyaga aylantiring: PHOTOS = ["1", "2", "3"]. Bosh sahifada uchta havola (/rasm/1 …). Ichkaridan ochilganda — modal: unda «Keyingi» tugmasi router.replace bilan keyingi rasmga o'tadi (tarixga qo'shmasdan — «Yopish» bosilganda bitta back() galereyaga qaytarsin). Oxirgi rasmda «Keyingi» chizilmasin. Modal matni: Rasm 2 / 3.

Yechim
tsx
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";

const PHOTOS = ["1", "2", "3"];
let soft = false;

function Modal({ id }: { id: string }) {
  const router = useRouter();
  const index = PHOTOS.indexOf(id);
  const next = PHOTOS[index + 1];
  return (
    <div role="dialog" aria-label={`Rasm ${id}`}>
      <p>
        Rasm {id} / {PHOTOS.length}
      </p>
      {next && (
        <button onClick={() => router.replace(`/rasm/${next}`)}>
          Keyingi
        </button>
      )}{" "}
      <button onClick={() => router.back()}>Yopish</button>
    </div>
  );
}

export default function App() {
  const path = usePathname();
  const id = path.startsWith("/rasm/") ? path.slice(6) : null;
  console.log(path);
  if (id && !soft) return <h1>Rasm {id} — to'liq sahifa</h1>;
  return (
    <>
      <h1>Galereya</h1>
      {PHOTOS.map((p) => (
        <span key={p}>
          <Link href={`/rasm/${p}`} onNavigate={() => (soft = true)}>
            Rasm {p}
          </Link>{" "}
        </span>
      ))}
      {id && <Modal id={id} />}
    </>
  );
}

Konsolda:

text
/
/rasm/1
/rasm/2
/

Ekranda:

text
Galereya
Rasm 1 Rasm 2 Rasm 3

Tarix: / → push /rasm/1 → replace /rasm/2. Tarixda ikki qadam qoldi (/, /rasm/2), shuning uchun bitta back() galereyaga qaytardi. push ishlatilsa, «Yopish» avval /rasm/1 ga qaytarardi — foydalanuvchi uchun g'alati: "yopdim, lekin oldingi rasm ochildi". Haqiqiy Next.js'da ham shunday, faqat ushlashni onNavigate emas, (.) papkasi hal qiladi.

4-mashq: Portfolio qadami

O'z portfolio ingizga loyihalar/layout.tsx ({children}{modal}), @modal/default.tsx, ushlovchi @modal/(.)[slug]/rasm/page.tsx, asl [slug]/rasm/page.tsx va RasmOynasi ni qo'shing. Har loyihaga skrinshot (public/loyihalar/<slug>.png). Tekshiring: next start da case study'dagi rasmni bosing — modal, URL /rasm bilan; Esc — URL qaytadi; sahifani /rasm da yangilang — to'liq sahifa. Keyin commit.

Yechim

Namuna — «Portfolio qadami» bo'limidagi fayllar va kanon kodi. Build Parallel route slots cannot render the same URLs bilan to'xtasa — default.tsx yo'q yoki noto'g'ri papkada (u aynan @modal/ ichida bo'lishi kerak). Interception routes must have a canonical route — asl rasm sahifasi yo'q. Modal o'rniga to'liq sahifa ochilsa — (.) darajasini «Kanondagi tuzilma» bo'limidagi daraxt bilan solishtiring. Fokus va Esc'ni klaviatura bilan sinang: Tab oynadan chiqib ketmasligi kerak.

8. Real ishda

  • Rasm va post modallari — Instagram, Pinterest, Dribbble naqshi. Do'konlarda mahsulot "tez ko'rish" oynasi: katalogdan bosilsa — modal, havola ulashilsa — to'liq mahsulot sahifasi.
  • Kirish oynasi — (...)kirish bilan istalgan sahifadan login modal'da ochiladi, /kirish ga to'g'ridan kirgan odam esa to'liq sahifani ko'radi.
  • Dashboard'lar — har panel alohida slot: o'z yuklanishi, o'z xatosi. Bitta sekin grafik butun panelni to'xtatmaydi.
  • Versiya yangilash — Next 16.4 dagi qat'iy marshrut tekshiruvi eski loyihalarda build'ni to'xtatishi mumkin. Xabarni o'qib, yetishmagan default.tsx ni qo'shish — eng tez yo'l (Versiyalarni yangilash).

Xulosa

  • @slot papka layout'ga children yonida prop bo'lib keladi; slot URL segmenti emas; har slot o'z loading/error'iga ega.
  • Soft navigatsiyada mos kelmagan slot eski holatini saqlaydi, hard navigatsiyada default.tsx chiziladi.
  • Next 16.4 qat'iy: default.tsx yo'q slot va asl sahifasiz ushlovchi marshrut — ikkalasi ham build xatosi.
  • (.), (..), (...) — URL segmentlari bo'yicha (slot va guruh sanalmaydi); ilova ichidan o'tishni ushlaydi.
  • URL'li modal = slot + ushlovchi marshrut + asl sahifa; yopish — router.back().

Keyingi dars: Server Components — portfolio sahifalari nega brauzerga JS yubormasligini va serverda nima bo'layotganini ochamiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Next.js parallel va intercepting routes: @slot, default.tsx va URL'li modal — IlmHamroh