Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Papka — segment, page.tsx — sahifa
- 2.1 Birinchi qoida
- 2.2 page.tsx siz papka — yopiq
- 2.3 Havola: <Link>
- 2.4 Natija oynasida: fayl-routing modeli
- 3. Dinamik segment: [slug]
- 3.1 Bitta fayl — ko'p loyiha
- 3.2 params — nega Promise?
- 3.3 PageProps — yo'lni biladigan tur
- 3.4 Natija oynasida: useParams
- 4. Hammasini ushlovchi segmentlar
- 4.1 [...qism] — qolgan hamma segment
- 4.2 [[...qism]] — bo'sh manzil ham
- 4.3 Biz topdik: params dekodlanmagan
- 4.4 Modelda: segmentlarni moslash
- 5. Qaysi papka yutadi?
- 5.1 Statik segment dinamikdan kuchli
- 5.2 Ikki xil ism bitta joyda — build xatosi
- 6. Haqiqiy build xatosi: Cache Components va params
- 6.1 Topilma: noma'lum loyiha — status 200
- 7. Portfolio qadami: v1 sahifalari Next.js'da
- 7.1 Fayllar
- 7.2 Tekshiruv
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): papka va manzil
- 2-mashq (o'rta): modelga blog qo'shing
- 3-mashq (qiyin): qo'llanma uchun "non yo'li"
- 4-mashq: Portfolio qadami
- 10. Real ishda
- Xulosa
- Manbalar
Next.js routing: page.tsx, papkalar va dinamik segmentlar ([slug], [...slug])
Qisqacha: Next.js'da marshrut ro'yxati yozilmaydi — uni papkalar beradi.
app/haqimda/page.tsxfayli/haqimdasahifasiga aylanadi: papka — URL segmenti,page.tsx— shu segmentning sahifasi. Kvadrat qavsdagi papka — dinamik segment:app/loyihalar/[slug]/page.tsxbitta fayl bilan/loyihalar/vazifalar,/loyihalar/bahorva boshqa hamma loyihaga xizmat qiladi. Qiymat sahifagaparamsorqali keladi, va u Next 15 dan beri Promise:const { slug } = await params.[...qism]— qolgan hamma segmentni massiv qilib ushlaydi,[[...qism]]— xuddi shunday, lekin bo'sh manzilni ham qabul qiladi.
Bu darsda
- Papka tuzilmasidan URL'ni (va aksincha) aytib bera olasiz;
page.tsxsiz papka nega ochilmasligini tushuntirasiz. [slug]dinamik segmentli sahifa yozasiz vaparamsniawaitbilan o'qiysiz.PageProps<"/loyihalar/[slug]">turi qayerdan kelishini va qanday xatoni ushlashini bilasiz.[...qism]va[[...qism]]farqini haqiqiy build natijasi bilan ko'rasiz.portfoliov2 ga v1 sahifalarini ko'chirasiz: bosh sahifa, «Men haqimda», «Loyihalar», loyiha sahifasi, «Aloqa».
Oldin bilishingiz kerak: Loyiha yaratish va tuzilishi, React Router II: dinamik va layout marshrutlar, React Router V: framework mode, async / await, Ko'p sahifali portfolio (faqat HTML).
1. Nega bu kerak?
Oldingi darsda create-next-app bo'sh skelet yaratdi: bitta app/page.tsx va bitta sahifa. Endi portfolio v1 dagi beshta sahifani ko'chirish kerak.
Eslang, 04-qismda v1 ni qanday qurgandingiz. Har sahifa — alohida papka, ichida index.html. haqimda/index.html — /haqimda/ manzili, loyihalar/bahor/index.html — /loyihalar/bahor/. Hech qanday "marshrutlar ro'yxati" yo'q edi: papkani ko'rib, manzilni bilardingiz. GitHub Pages ham shunday ishlaydi.
React Router da esa boshqacha edi. Har sahifa uchun marshrutlar.tsx ga bitta obyekt yozdingiz: { path: "/taom/:slug", element: <Taom /> }. Fayl qayerda turishi muhim emas — ro'yxat hal qiladi.
Next.js birinchi yo'lni tanladi, lekin unga React qo'shdi. Papka — manzil, xuddi v1 dagidek. Faqat index.html o'rnida page.tsx — React komponenti turadi. Bu fayl-asosli routing (file-based routing) deyiladi: marshrut fayl tizimidan o'qiladi.
Hayotiy o'xshatish: ko'p qavatli uyning pochta qutilari. Har qutida xonadon raqami yozilgan. Pochtachi ro'yxat ochmaydi — qutini topadi va xatni tashlaydi. Qutisi yo'q xonadonga xat bormaydi. Next.js'da quti — page.tsx.
v1 dagi loyihalar/bahor/ papkasida yana bir muammo bor edi. Har yangi loyiha uchun yangi papka va yangi HTML nusxasi kerak edi. Uchinchi loyihada sarlavha o'zgarsa, uchta faylni qo'lda tuzatardingiz. Bugun buni bitta fayl bilan hal qilamiz.
2. Papka — segment, page.tsx — sahifa
2.1 Birinchi qoida
Segment — URL'ning ikki / orasidagi bo'lagi (React Router II da ko'rgansiz): /loyihalar/bahor da ikkita segment — loyihalar va bahor. Next.js'da app/ ichidagi har papka — bitta segment. Papka ichida page.tsx bo'lsa, shu manzil ochiladi.
Rasmda — portfolio ning bugungi app/ papkasi va undan chiqqan manzillar.
flowchart LR
A["app/page.tsx"] --> U1["/"]
B["app/haqimda/page.tsx"] --> U2["/haqimda"]
C["app/loyihalar/page.tsx"] --> U3["/loyihalar"]
D["app/loyihalar/[slug]/page.tsx"] --> U4["/loyihalar/vazifalar<br/>/loyihalar/bahor …"]
E["app/aloqa/page.tsx"] --> U5["/aloqa"]Diqqat qiling: app/page.tsx — ildiz /. app papkasining o'zi segment emas, u — boshlanish nuqtasi. [slug] papkasi esa bitta emas, ko'p manzil beradi — uni keyingi bo'limda ochamiz.
portfolio kanonidagi «Aloqa» sahifasi to'liq shunday:
import type { Metadata } from "next";
export const metadata: Metadata = { title: "Aloqa — Aziz Karimov" };
export default function AloqaSahifasi() {
return (
<main id="asosiy">
<h1>Aloqa</h1>
<p>
Ish taklifi yoki savol bo'lsa, yozing:{" "}
<a href="mailto:aziz@example.com">aziz@example.com</a> yoki{" "}
<a href="https://t.me/sizning_kanal">Telegram</a>.
</p>
{/* … ko'p so'raladigan savollar — details tegi bilan */}
</main>
);
}Uchta narsaga qarang:
| Qism | Nima qiladi |
|---|---|
export default |
Next.js sahifa sifatida aynan shuni chizadi |
AloqaSahifasi |
Nomi ixtiyoriy — URL'ga ta'sir qilmaydi |
metadata |
Brauzer tabidagi sarlavha (Metadata API da chuqur) |
Funksiya nomi URL'ni belgilamaydi. Manzilni faqat papka yo'li belgilaydi. AloqaSahifasi ni Page deb atasangiz ham, sahifa baribir /aloqa da qoladi.
Bu sahifada useState ham, onClick ham yo'q. Next.js'da sahifa sukut bo'yicha serverda chiziladigan Server Component bo'ladi — brauzerga tayyor HTML boradi. Buni Server Components darsida to'liq o'rganamiz. Hozircha bilish yetarli: sahifa — oddiy React komponent, faqat u serverda ishlaydi.
Tekshirib ko'ring:
app/blog/yangiliklar/page.tsxfayli qaysi manzilni ochadi? Funksiyaniexport default function Salom()deb atasak, manzil o'zgaradimi?
Javob
/blog/yangiliklar — ikkita papka, ikkita segment. Funksiya nomi manzilga ta'sir qilmaydi, faqat export default bo'lishi muhim.
2.2 page.tsx siz papka — yopiq
Papka yaratishning o'zi manzil ochmaydi. Biz tajriba qildik: app/sinov/ papkasiga faqat Tugma.tsx komponentini qo'ydik, page.tsx qo'ymadik. Keyin next build va next start, so'ng ikki manzilni so'radik:
/sinov -> 404
/sinov/Tugma -> 404Ikkalasi ham 404 (topilmadi). Build jadvalida /sinov umuman yo'q. Ya'ni app/ ichida sahifaga tegishli komponentni (masalan faqat shu bo'limda ishlatiladigan kartani) yonma-yon saqlash xavfsiz — u o'z-o'zidan sahifa bo'lib qolmaydi. Bu odatning nomi colocation (yonma-yon joylash); uni Route groups va private papkalar darsida tartibga solamiz.
Ikkinchi tajriba: page.tsx bor, lekin ichida export default yo'q, faqat nomli eksport (export function Sinov()). next build to'xtadi:
.next/types/validator.ts(184,31): error TS2344: Type 'typeof import("C:/Users/…/p/src/app/sinov/page")' does not satisfy the constraint 'AppPageConfig<"/sinov">'.
Failed to type check.Tarjimasi: "sinov/page modulining turi AppPageConfig<"/sinov"> shartiga mos kelmaydi". Xabar validator.ts degan begona faylni ko'rsatadi — chalg'imang. Bu faylni Next.js o'zi yaratadi va unda har page.tsx "to'g'ri sahifami?" deb tekshiriladi. Asl sabab xabardagi yo'lda: src/app/sinov/page. Qoida: page.tsx da doim export default.
2.3 Havola: <Link>
Sahifalar orasida o'tish uchun next/link dan Link komponenti ishlatiladi. U oddiy <a> chizadi, lekin bosilganda sahifani to'liq qayta yuklamaydi — React Router dagi <Link to> kabi. Farqi bitta: atribut nomi href.
import Link from "next/link";
<Link href="/loyihalar">Loyihalar</Link>Link ning to'liq imkoniyatlari (oldindan yuklash, replace, scroll) va useRouter — Navigatsiya darsida. Bugun faqat "manzilni o'zgartiradigan havola" sifatida ishlatamiz.
2.4 Natija oynasida: fayl-routing modeli
Saytdagi natija oynasida server yo'q va app/ papkasi ham yo'q. Shuning uchun Next.js'ning o'zini u yerda ishga tushirib bo'lmaydi. Lekin next/link va next/navigation uchun IlmHamroh'da kichik o'quv modeli bor: <Link>, usePathname(), useParams() va boshqa hook'lar xotiradagi manzil bilan ishlaydi.
Diqqat: natija oynasidagi
next/link— o'quv modeli, haqiqiy Next.js emas. Haqiqiy Next.js'da havola ortida server, oldindan yuklash (prefetch) va RSC so'rovi bor. Modelda esa faqat xotiradagi manzil o'zgaradi. Manzil satri ham yo'q — joriy manzilni ko'rish uchunusePathname()ni ekranga chiqaramiz.
Quyidagi blokda Next.js ichida nima bo'layotganini qo'lda qilamiz. FILES obyekti — "app papkasi": kalit — papka yo'li, qiymat — sahifa komponenti. App joriy manzilni oladi va mos "faylni" topadi.
import Link from "next/link";
import { usePathname } from "next/navigation";
import type { ReactNode } from "react";
// "app/" papkasi: kalit — page.tsx turgan papka yo'li
const FILES: Partial<Record<string, () => ReactNode>> = {
"/": () => <h1>Bosh sahifa</h1>,
"/haqimda": () => <h1>Men haqimda</h1>,
"/loyihalar": () => <h1>Loyihalar</h1>,
};
export default function App() {
const pathname = usePathname();
const Page = FILES[pathname];
console.log(pathname, Page ? "topildi" : "404");
return (
<>
<nav>
<Link href="/">Bosh sahifa</Link>{" "}
<Link href="/haqimda">Men haqimda</Link>{" "}
<Link href="/loyihalar">Loyihalar</Link>
</nav>
<p>Manzil: {pathname}</p>
{Page ? <Page /> : <h1>404 — sahifa topilmadi</h1>}
</>
);
}Konsolda:
/ topildi
/loyihalar topildi
/haqimda topildiEkranda:
Bosh sahifa Men haqimda Loyihalar
Manzil: /haqimda
Men haqimda«Ishga tushir» ni bosing va havolalarni bosib ko'ring. Har bosishda usePathname() yangi manzilni qaytaradi, App qayta chiziladi va FILES dan boshqa sahifa olinadi. Next.js aynan shu ishni qiladi, faqat FILES ni u app/ papkasidan o'zi yig'adi. Siz obyektga qator yozmaysiz — papka yaratasiz.
O'zingiz sinab ko'ring: FILES ga "/aloqa" qatorini va menyuga havolasini qo'shing. Keyin "/haqimda" qatorini o'chiring — «Men haqimda» bosilganda "404" chiqadi. Xuddi page.tsx ni o'chirgandek.
3. Dinamik segment: [slug]
3.1 Bitta fayl — ko'p loyiha
v1 da har loyiha — alohida papka edi. Next.js'da buning o'rniga papka nomini kvadrat qavsga olasiz: app/loyihalar/[slug]/. Bu dinamik segment (dynamic segment): "shu joyga istalgan qiymat tushadi, uni slug deb ata". React Router'dagi :slug ning aynan o'zi, faqat yozilishi boshqa.
| React Router | Next.js | |
|---|---|---|
| Dinamik segment | path: "/loyihalar/:slug" |
app/loyihalar/[slug]/page.tsx |
| Qiymatni o'qish | useParams() |
params prop (sahifada) |
| Qiymat turi | satr | satr |
Qavs ichidagi nom — sizning tanlovingiz: [slug], [id], [nomi]. Shu nom params obyektidagi kalit bo'ladi.
portfolio kanonidagi loyiha sahifasining boshi (pastki qismi — case study jadvali, darsga kerak emas):
import { LOYIHALAR, loyihaniTop } from "@/malumot/loyihalar";
import { oyniYoz } from "@/malumot/sana";
// Cache Components yoqilgan: params — "runtime data". Ro'yxat
// oldindan ma'lum bo'lsa, build vaqtida hammasi yaratiladi
// (N-17 da chuqurroq); aks holda build "uncached or runtime data
// during prerendering" bilan to'xtaydi.
export function generateStaticParams() {
return LOYIHALAR.map((l) => ({ slug: l.slug }));
}
// Dinamik segment: /loyihalar/vazifalar → params.slug ===
// "vazifalar" (N-03). Next 15+ da params — Promise.
export default async function LoyihaSahifasi({
params,
}: PageProps<"/loyihalar/[slug]">) {
const { slug } = await params;
const loyiha = loyihaniTop(slug);
if (!loyiha) {
return (
<main id="asosiy">
<h1>Loyiha topilmadi</h1>
</main>
);
}
// … sarlavha, sana, texnologiyalar, case study
}Kanondagi izohlar telefonga sig'ishi uchun qatorlarga bo'lindi, ma'nosi o'sha. Qatorma-qator:
generateStaticParams— "qaysisluglar borligini build vaqtida ayt" funksiyasi. Uni generateStaticParams darsida chuqur ochamiz; nega bu yerda shartligini pastda «Haqiqiy build xatosi» bo'limida ko'rasiz.async function— sahifa asinxron komponent. Bu faqat Server Component'da mumkin; oddiy React'da (natija oynasida ham) komponentasyncbo'la olmaydi.await params— manzildagi qiymat./loyihalar/vazifalardaslug—"vazifalar".loyihaniTop(slug)—src/malumot/loyihalar.tsdagi ro'yxatdan qidiradi. Topilmasa — "Loyiha topilmadi".
Ma'lumot fayli oddiy TypeScript massivi: uchta loyiha (vazifalar, bahor, landing), har birida slug, nomi, sana, texnologiyalar. Yangi loyiha qo'shish — massivga bitta obyekt. Sahifa fayliga tegmaysiz.
3.2 params — nega Promise?
async / await darsidan bilasiz: Promise — "qiymat keyin keladi" degan va'da. Next.js 15 dan boshlab params ham shunday. Sabab — tezlik: Next.js sahifaning manzilga bog'liq bo'lmagan qismini (sarlavha, menyu) params tayyor bo'lishini kutmasdan chiza boshlaydi. await params yozilgan joyda esa haqiqatan kutadi.
Bu bloklar sahifa funksiyasini oddiy funksiya sifatida chaqiradi — Next.js ham ichida shunday qiladi:
async function ProjectPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return `Loyiha: ${slug}`;
}
const params = Promise.resolve({ slug: "vazifalar" });
ProjectPage({ params }).then((html) => console.log(html));Konsolda:
Loyiha: vazifalarPromise.resolve(...) — darhol tayyor bo'lgan va'da. Next.js params ni xuddi shunday Promise qilib beradi.
Eng ko'p uchraydigan xato — await ni unutish. Eski (Next 14) darslik va maqolalarda params.slug to'g'ridan-to'g'ri yozilgan. TypeScript buni ushlaydi:
async function ProjectPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
return `Loyiha: ${params.slug}`;
}Xato:
TS2339: Property 'slug' does not exist on type 'Promise<{ slug: string; }>'.Tarjimasi: "Promise<{ slug: string }> turida slug xususiyati yo'q". To'g'ri: slug Promise'ning ichida, Promise'ning o'zida emas. Qutini ochmasdan ichidagi narsani ololmaysiz — await qutini ochadi.
TypeScript bo'lmasa-chi? Biz tajriba qildik: Next 16.4 da params ni turini chetlab o'tib (as unknown as …) to'g'ridan o'qidik. Sahifa yiqilmadi, lekin qiymat bo'sh:
/sinxron/vazifalar -> 200 SINXRON=undefined PROMISE=true AWAIT=vazifalarparams.slug — undefined, params instanceof Promise — true, await bilan esa "vazifalar". Next 15 da eski kod uchun vaqtinchalik ko'prik (ogohlantirish bilan ishlash) bor edi; Next 16 da u olib tashlangan. Jim undefined — eng yomon xato turi: sahifa "Loyiha topilmadi" deydi, sababi ko'rinmaydi.
Tekshirib ko'ring:
/loyihalar/bahorochilgandaawait paramsnima qaytaradi?/loyihalarochilganda-chi?
Javob
{ slug: "bahor" }. /loyihalar esa [slug] papkasiga umuman tushmaydi — u app/loyihalar/page.tsx (ro'yxat sahifasi). Dinamik segment bo'sh bo'la olmaydi: [slug] uchun manzilda shu joyda albatta bir segment bo'lishi kerak.
3.3 PageProps — yo'lni biladigan tur
Kanonda sahifa props turi qo'lda yozilmagan: PageProps<"/loyihalar/[slug]">. Bu — global tur, import kerak emas. Uni next typegen buyrug'i yaratadi: Next.js app/ papkasini o'qiydi va har marshrut uchun aniq tur yozadi. portfolio da bu buyruq npm run tip skriptining ichida:
npm run tip> portfolio@2.0.0 tip
> next typegen && tsc --noEmit
Generating route types...
✓ Types generated successfullyFoydasi — yo'l xato yozilsa, TypeScript darhol aytadi. Biz ataylab ikki xato qildik: birida await ni unutdik, ikkinchisida yo'lni "/xato/[slug]" deb yozdik (papka aslida xato2/[slug]):
src/app/xato1/[slug]/page.tsx(2,24): error TS2339: Property 'slug' does not exist on type 'Promise<{ slug: string; }>'.
src/app/xato2/[slug]/page.tsx(1,55): error TS2344: Type '"/xato/[slug]"' does not satisfy the constraint 'AppRoutes'.
src/app/xato2/[slug]/page.tsx(2,11): error TS2339: Property 'slug' does not exist on type 'unknown'.Ikkinchi xabar: ""/xato/[slug]" turi AppRoutes shartiga mos kelmaydi". AppRoutes — loyihadagi hamma marshrutlar ro'yxati (typegen yozgan). Bunday yo'l yo'q — demak xato. Uchinchi xabar ikkinchisining natijasi: tur topilmagani uchun params ichi unknown bo'lib qoldi. Qoida: xatolar zanjirida birinchisini tuzating, qolgani o'zi yo'qoladi.
Muhim: papka qo'shgach yoki nomini o'zgartirgach, turlar eskiradi. next dev ishlab turganda ular o'zi yangilanadi; aks holda npm run tip ni qayta ishga tushiring.
3.4 Natija oynasida: useParams
Sahifa komponenti params ni prop sifatida oladi. Lekin sahifa ichidagi kichik interaktiv komponent (masalan «Ulashish» tugmasi) ham slug ni bilishi kerak bo'lsa-chi? Unga useParams() hook'i bor — React Router'dagidek. Faqat u brauzerda ishlaydigan client komponentda ishlaydi (Client Components darsida), va unda Promise yo'q — qiymat darhol keladi.
Natija oynasida boshlang'ich manzil har doim /. Boshqa manzil va dinamik segment kerak bo'lsa, model sozlamasi bor: ih.boshla(manzil, shablon). Shablon — page.tsx turgan papka yo'li, masalan "/loyihalar/[slug]". Bu Next.js API'si emas, faqat o'quv modelining tugmasi — haqiqiy loyihada bunday qator yozilmaydi. Next.js shablonni app/ papkasidan o'zi biladi.
import { ih, useParams, usePathname } from "next/navigation";
// Faqat o'quv modeli: /loyihalar/vazifalar sahifasidamiz,
// papka yo'li — app/loyihalar/[slug]/page.tsx
ih.boshla("/loyihalar/vazifalar", "/loyihalar/[slug]");
function ShareButton() {
const { slug } = useParams<{ slug: string }>();
return <button>«{slug}» havolasini nusxalash</button>;
}
export default function App() {
const pathname = usePathname();
return (
<main>
<p>Manzil: {pathname}</p>
<ShareButton />
</main>
);
}Ekranda:
Manzil: /loyihalar/vazifalar
«vazifalar» havolasini nusxalashModel manzilni shablon bilan solishtiradi: [slug] turgan joydagi segment — slug. Shuning uchun <Link> bilan /loyihalar/bahor ga o'tsangiz, useParams() { slug: "bahor" } beradi — haqiqiy Next.js'dagidek. Farq shundaki, Next.js shablonlarni app/ papkasidan o'zi yig'adi, modelga esa uni qo'lda aytamiz.
4. Hammasini ushlovchi segmentlar
4.1 [...qism] — qolgan hamma segment
Faraz qiling, portfolio da hujjatlar bo'limi bor: /hujjatlar/kirish, /hujjatlar/next/routing, /hujjatlar/next/routing/misollar. Chuqurlik oldindan noma'lum. [slug] faqat bitta segment ushlaydi — bu yerda yetmaydi.
Uch nuqtali dinamik segment — hammasini ushlovchi segment (catch-all): app/hujjatlar/[...qism]/page.tsx. U shu joydan boshlab qolgan hamma segmentni oladi va massiv qilib beradi. React Router'dagi * ning Next.js'dagi o'xshashi, faqat nomli.
Biz tajriba loyihasida shunday sahifa yozdik va next build + next start bilan so'radik. Sahifa JSON.stringify(await params) ni chiqarardi:
/hujjatlar/kirish -> 200 PARAMS={"qism":["kirish"]}
/hujjatlar/next/routing -> 200 PARAMS={"qism":["next","routing"]}
/hujjatlar/a/b/c -> 200 PARAMS={"qism":["a","b","c"]}
/hujjatlar -> 404Uchta narsa:
- Bitta segment bo'lsa ham — massiv:
["kirish"], satr emas. /hujjatlar/a/b/c— oldindan tayyorlanmagan manzil ham ishladi (status 200)./hujjatlarning o'zi — 404. Hammasini ushlovchi segment kamida bitta segment talab qiladi.
4.2 [[...qism]] — bo'sh manzil ham
Ikki qavat qavs — ixtiyoriy hammasini ushlovchi segment (optional catch-all): app/qollanma/[[...qism]]/page.tsx. Farqi bitta: /qollanma ning o'zi ham shu sahifaga tushadi. Tajriba:
/qollanma -> 200 PARAMS={} kalitlar= qism=undefined
/qollanma/a/b -> 200 PARAMS={"qism":["a","b"]} kalitlar=qism qism=a,b
/qollanma/x -> 200 PARAMS={"qism":["x"]} kalitlar=qism qism=x/qollanma da params — bo'sh obyekt {}: qism kaliti umuman yo'q, params.qism — undefined. Bo'sh massiv [] emas! Kodda buni tekshirish shart: qism?.length ?? 0 yoki qism ?? [].
| Papka | /hujjatlar |
/hujjatlar/a |
/hujjatlar/a/b |
|---|---|---|---|
[slug] |
404 | { slug: "a" } |
404 |
[...qism] |
404 | { qism: ["a"] } |
{ qism: ["a", "b"] } |
[[...qism]] |
{} |
{ qism: ["a"] } |
{ qism: ["a", "b"] } |
4.3 Biz topdik: params dekodlanmagan
Tajribada yana bir narsa chiqdi. Segmentda bo'sh joy yoki maxsus harf bo'lsa, brauzer uni foiz-kodlash (percent-encoding) bilan yuboradi: bo'sh joy — %20 (URL va URLSearchParams darsida ko'rgansiz). Next 16.4 dagi params da u shu holicha qoldi:
/hujjatlar/salom%20dunyo -> 200 PARAMS={"qism":["salom%20dunyo"]}
/hujjatlar/o%CA%BBzbek -> 200 PARAMS={"qism":["o%CA%BBzbek"]}
/hujjatlar/a?x=1 -> 200 PARAMS={"qism":["a"]}"salom dunyo" emas, "salom%20dunyo". Bunday qiymatni ekranga chiqarishdan oldin decodeURIComponent dan o'tkazing. Oxirgi qatorga ham qarang: ?x=1 — query qismi, segment emas; u params ga kirmaydi (uni searchParams o'qiydi, Navigatsiya darsida).
portfolio slug'lari faqat lotin harf va - dan iborat (vazifalar, bahor), shuning uchun kanonda bu muammo chiqmaydi. Lekin o'quvchi slug'ga kirill yoki ' qo'ysa, darhol chiqadi.
4.4 Modelda: segmentlarni moslash
Next.js qanday qilib /hujjatlar/next/routing ni [...qism] ga moslaydi? Quyidagi funksiya — soddalashtirilgan model (Next.js'ning haqiqiy kodi emas), lekin natijalari yuqoridagi haqiqiy tajribaga to'liq mos:
function matchRoute(pattern, pathname) {
const pat = pattern.split("/").filter(Boolean);
const url = pathname.split("/").filter(Boolean);
const params = {};
for (let i = 0; i < pat.length; i++) {
const seg = pat[i];
const rest = url.slice(i);
if (seg.startsWith("[[...")) {
if (rest.length > 0) params[seg.slice(5, -2)] = rest;
return params;
}
if (seg.startsWith("[...")) {
if (rest.length === 0) return null;
params[seg.slice(4, -1)] = rest;
return params;
}
if (url[i] === undefined) return null;
if (seg.startsWith("[")) params[seg.slice(1, -1)] = url[i];
else if (seg !== url[i]) return null;
}
return url.length === pat.length ? params : null;
}
const show = (pattern, path) =>
console.log(path, JSON.stringify(matchRoute(pattern, path)));
show("/loyihalar/[slug]", "/loyihalar/bahor");
show("/loyihalar/[slug]", "/loyihalar/bahor/rasm");
show("/hujjatlar/[...qism]", "/hujjatlar/next/routing");
show("/hujjatlar/[...qism]", "/hujjatlar");
show("/qollanma/[[...qism]]", "/qollanma");Konsolda:
/loyihalar/bahor {"slug":"bahor"}
/loyihalar/bahor/rasm null
/hujjatlar/next/routing {"qism":["next","routing"]}
/hujjatlar null
/qollanma {}null — "mos kelmadi" (Next.js'da — 404). Har shablon segmentini manzil segmenti bilan yonma-yon solishtiramiz: oddiy nom — aynan teng bo'lishi kerak, [x] — istalgan bitta qiymat, [...x] — qolgan hammasi (kamida bitta), [[...x]] — qolgan hammasi (nol ham mumkin).
O'zingiz sinab ko'ring: show("/qollanma/[[...qism]]", "/qollanma/a/b") qatorini qo'shing. Natija tajribadagi {"qism":["a","b"]} bilan bir xil chiqishi kerak.
5. Qaysi papka yutadi?
5.1 Statik segment dinamikdan kuchli
app/loyihalar/[slug]/ bor. Endi app/loyihalar/yangi/page.tsx ni ham qo'shsak, /loyihalar/yangi qaysi sahifani ochadi? Ikkalasi ham mos keladi-ku.
Tajriba javobi:
/loyihalar/yangi -> 200 STATIK yangi sahifaQoida React Router'dagidek: aniq nom har doim yutadi. Tartib — statik segment, keyin [slug], keyin [...qism], oxiri [[...qism]]. Papkalarni qaysi tartibda yaratganingiz ahamiyatsiz — Next.js o'zi saralaydi.
5.2 Ikki xil ism bitta joyda — build xatosi
Bir darajada ikkita dinamik papka bo'lsa-chi? app/loyihalar/[slug]/ yoniga [id]/ qo'shdik:
> Build error occurred
Error: Ambiguous app routes detected:
Ambiguous route pattern "/loyihalar/[*]" matches multiple routes:
- /loyihalar/[id]
- /loyihalar/[slug]
These routes cannot be distinguished from each other when matching URLs. Please ensure that dynamic segments have unique patterns or use different static segments.Tarjimasi: "Noaniq marshrutlar topildi. /loyihalar/[*] shabloni bir nechta marshrutga mos keladi. Manzilni moslashda ularni bir-biridan ajratib bo'lmaydi." Haqiqatan: /loyihalar/bahor — bu id mi yoki slug mi? Ajratib bo'lmaydi. Yechim — bitta dinamik papka yoki boshqa statik segment (/loyihalar/raqam/[id]).
Ikkinchi to'qnashuv: app/qollanma/page.tsx va app/qollanma/[[...qism]]/ birga:
> Build error occurred
Error: You cannot define a route with the same specificity as an optional catch-all route ("/qollanma" and "/qollanma/[[...qism]]")."Ixtiyoriy hammasini ushlovchi segment bilan bir xil aniqlikdagi marshrut yozib bo'lmaydi". Sababi «[[...qism]] — bo'sh manzil ham» bo'limidagi jadvalda: [[...qism]] /qollanma ning o'zini ham oladi — page.tsx bilan ikkalasi bitta manzilga da'vo qiladi. Yechim — bittasini tanlang: yo [[...qism]], yo page.tsx + [...qism].
Tekshirib ko'ring: papkalar
app/blog/page.tsx,app/blog/[slug]/page.tsx,app/blog/arxiv/page.tsx./blog/arxiv,/blog/salomva/blog/2026/10qaysi sahifani ochadi?
Javob
/blog/arxiv — statik arxiv (aniq nom yutadi). /blog/salom — [slug], { slug: "salom" }. /blog/2026/10 — 404: [slug] faqat bitta segment oladi, ikkitasi uchun [...qism] kerak edi.
6. Haqiqiy build xatosi: Cache Components va params
portfolio skeleti create-next-app 16.4 dan keldi va next.config.ts da Cache Components yoqilgan (cacheComponents: true). Bu rejimda Next.js har sahifani build vaqtida oldindan tayyorlashga urinadi. [slug] sahifasi esa params ni kutadi — build vaqtida qaysi slug'lar borligi noma'lum.
Kanon generateStaticParams ni yozmasdan build qilib ko'rdi. Natija:
✓ Compiled successfully in 2.3s
Error: Route "/loyihalar/[slug]": Next.js encountered uncached or runtime data during prerendering.
- Start the app in development mode by running `next dev`, then open "/loyihalar/[slug]" in your browser to investigate the error.
Error occurred prerendering page "/loyihalar/[slug]". Read more: https://nextjs.org/docs/messages/prerender-error
Export encountered an error on /loyihalar/[slug]/page: /loyihalar/[slug], exiting the build.Tarjimasi: "/loyihalar/[slug] marshruti: oldindan chizish (prerender) paytida Next.js keshlanmagan yoki ish vaqtidagi (runtime) ma'lumotga duch keldi". Bu yerda "ish vaqtidagi ma'lumot" — aynan params. Next 15 da Next.js bunday sahifani jimgina "har so'rovda chiziladigan" qilardi. Next 16 + Cache Components esa jim qolmaydi: qaror qabul qilishni so'raydi.
Bir yechim — generateStaticParams(): build'ga "3 ta slug bor" deb aytish. Kanon shuni tanladi va build o'tdi:
Route (app)
┌ ○ /
├ ○ /_not-found
├ ○ /aloqa
├ ○ /haqimda
├ ○ /icon.svg
├ ○ /loyihalar
└ /loyihalar/[slug]
├ ◐ /loyihalar/[slug]
├ ○ /loyihalar/vazifalar
├ ○ /loyihalar/bahor
└ ○ /loyihalar/landing
○ (Static) prerendered as static content
◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed contentJadvalni o'qing:
| Belgi | Ma'nosi |
|---|---|
○ |
Statik — build vaqtida tayyor HTML (v1 dagi index.html kabi) |
◐ |
PPR (Partial Prerendering — qisman prerender): tayyor qobiq, noma'lum slug kelsa qolgani so'rovda chiziladi |
Uchta loyiha — ○: tayyor fayllar. ◐ /loyihalar/[slug] — qolgan har qanday slug uchun qobiq (shell). Rendering turlari Rendering strategiyalari va Cache Components I darslarida to'liq ochiladi; bugun belgilarni tanish yetarli.
Next 16.4 build jadvalida "Size" va "First Load JS" ustunlari yo'q (eski versiyalarda bor edi). Shuning uchun kanon JS hajmini brauzerda o'lchaydi.
6.1 Topilma: noma'lum loyiha — status 200
Kanon next start ustida har sahifani so'radi. Bitta qator tashvishli:
### /loyihalar/yoq — status 200, TTFB 119 ms, DCL 518 ms; JS 6 fayl 135.4 KB tarmoq (gzip), 454.2 KB xom, CSS 1.8 KB, shrift 0.0 KB
axe: 0
konsol: 0 (hydration 0)
cache-control: private, no-cache, no-store, max-age=0, must-revalidate
x-nextjs-prerender: 1Mavjud bo'lmagan loyiha — 200 (muvaffaqiyat) statusi bilan "Loyiha topilmadi" sahifasi. Odam uchun to'g'ri ko'rinadi. Google uchun esa bu "haqiqiy sahifa": u /loyihalar/yoq ni indekslashi mumkin. Bunday sahifaning nomi — soft 404 (yumshoq 404). To'g'ri yo'l — Next.js'ning notFound() funksiyasi va 404 statusi. Uni keyingi darsdan keyin qo'shamiz, kanon ham shunday qiladi.
Tekshirib ko'ring: build jadvalida
/loyihalar/vazifalaroldida qaysi belgi turibdi va bu nima degani?
Javob
○ — statik: build vaqtida tayyor HTML yaratilgan, har so'rovda qayta chizilmaydi. Buni generateStaticParams ta'minladi — u vazifalar slug'ini build'ga oldindan aytdi.
7. Portfolio qadami: v1 sahifalari Next.js'da
Bu dars va keyingi dars portfolio ning bitta holatini quradi. Bugun — sahifalar va dinamik segment. Layout va template (header, footer, kirish animatsiyasi) — keyingi darsda, commit ham o'sha yerda.
7.1 Fayllar
src/
├── app/
│ ├── page.tsx → /
│ ├── haqimda/page.tsx → /haqimda
│ ├── loyihalar/
│ │ ├── page.tsx → /loyihalar
│ │ └── [slug]/page.tsx → /loyihalar/vazifalar …
│ └── aloqa/page.tsx → /aloqa
├── komponentlar/
│ └── LoyihaKartasi.tsx
└── malumot/
├── loyihalar.ts (3 loyiha)
└── sana.ts (oyniYoz)v1 dagi loyihalar/bahor/index.html endi alohida papka emas — u loyihalar.ts dagi bitta obyekt. vazifalar (09–22-qismlardagi ilovangiz) case study bilan birinchi loyiha bo'ldi: vazifa, yechim bosqichlari, raqamlar jadvali, saboqlar.
sana.ts dagi oyniYoz("2026-09") — "2026-yil sentabr". Nega Intl.DateTimeFormat emas? 21-qismda Chrome Intl o'zbek lotin sanasini to'g'ri chiqarmasligini topgandik. Kanon oy nomlarini qo'lda yozdi. Bu qaror Hydration darsida yana bir sababga ega bo'ladi.
Ro'yxat sahifasidagi kartalar LoyihaKartasi komponentida. Kartada sarlavha — <Link href={/loyihalar/${loyiha.slug}}>: dinamik segmentga havola oddiy shablon satri bilan yasaladi.
7.2 Tekshiruv
npm run lint, npm run tip, npm run format:check — 0 xato. Build — yuqoridagi jadval (8 s). Kanonning brauzer o'lchovi:
| Sahifa | Status | JS (gzip) |
|---|---|---|
/, /haqimda, /loyihalar, /aloqa |
200 | 135.4 KB |
/loyihalar/vazifalar, bahor, landing |
200 | 135.4 KB |
/loyihalar/yoq |
200 (xato — keyingi qadam) | 135.4 KB |
/yoq |
404 | 135.4 KB |
JS bo'sh skeletdagi 132.4 KB dan 135.4 KB ga oshdi: +3.0 KB — next/link. TTFB (birinchi bayt vaqti) statik sahifalarda 4–12 ms. axe — 0 buzilish (faqat /yoq da 3 ta: Next.js'ning standart 404 sahifasida main yo'q — maxsus fayllar darsida o'zimiznikini yozamiz).
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Papka bor, sahifa 404 | Ichida page.tsx yo'q (yoki Page.tsx, index.tsx) |
Fayl nomi aynan page.tsx |
TS2344 … AppPageConfig |
page.tsx da export default yo'q |
export default function … |
Property 'slug' does not exist on type 'Promise<…>' |
await params unutilgan |
const { slug } = await params |
slug — undefined, xato yo'q |
params.slug turini chetlab o'qilgan |
await; as bilan tur aldamang |
TS2344 … 'AppRoutes' |
PageProps dagi yo'l papkaga mos emas |
Papka yo'lini aynan yozing, npm run tip |
Ambiguous app routes detected |
[id] va [slug] bir joyda |
Bitta dinamik papka |
… same specificity as an optional catch-all route |
page.tsx va [[...x]] birga |
Bittasini tanlang |
uncached or runtime data during prerendering |
Cache Components + params, ro'yxat yo'q |
generateStaticParams |
Matnda %20 |
params dekodlanmagan |
decodeURIComponent |
qism.length da TypeError |
[[...qism]] ning bo'sh manzilida qism yo'q |
qism ?? [] |
9. Mashqlar
Mashqlar kurs/mashqlar/23/03-routing/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): papka va manzil
app/ ichidagi papkalar: page.tsx, blog/page.tsx, blog/[slug]/page.tsx, dokon/[...kategoriya]/page.tsx, yordam/[[...mavzu]]/page.tsx.
/blog/salom→params:{ slug: "" }/dokon/kiyim/erkaklar→{ kategoriya: ] }/dokon→ status/yordam→params:/yordam/tolov→{ mavzu: ] }
Yechim
- salom —
[slug]bitta segment oladi, satr sifatida. - ["kiyim", "erkaklar"] — hammasini ushlovchi segment doim massiv beradi.
- 404 —
[...kategoriya]kamida bitta segment talab qiladi,dokon/page.tsxesa yo'q. - {} — ixtiyoriy variantda bo'sh manzil ham mos, lekin kalit umuman yo'q (
[]emas). - ["tolov"] — bitta segment bo'lsa ham massiv.
2-mashq (o'rta): modelga blog qo'shing
«Fayl-routing modeli» bo'limidagi blokni oling. FILES ga "/blog" sahifasini qo'shing: unda <Link href="/blog/salom"> havolasi bo'lsin. Keyin /blog/[slug] uchun sahifa qo'shing — u "Maqola: salom" deb yozsin. Buning uchun App da FILES[pathname] topilmasa, pathname ni /blog/ bilan boshlanishini tekshiring va qolgan qismini slug deb oling (pathname.slice(6) — satr metodlari). Ichma-ich / bo'lsa (/blog/a/b) — 404.
Yechim
import Link from "next/link";
import { usePathname } from "next/navigation";
import type { ReactNode } from "react";
const FILES: Partial<Record<string, () => ReactNode>> = {
"/": () => <h1>Bosh sahifa</h1>,
"/blog": () => (
<>
<h1>Blog</h1>
<Link href="/blog/salom">Salom maqolasi</Link>
</>
),
};
// app/blog/[slug]/page.tsx o'rnida
function BlogPost({ params }: { params: { slug: string } }) {
return <h1>Maqola: {params.slug}</h1>;
}
function findPage(pathname: string): ReactNode {
const Page = FILES[pathname];
if (Page) return <Page />;
if (pathname.startsWith("/blog/")) {
const slug = pathname.slice(6);
if (slug && !slug.includes("/")) {
return <BlogPost params={{ slug }} />;
}
}
return <h1>404 — sahifa topilmadi</h1>;
}
export default function App() {
const pathname = usePathname();
return (
<>
<nav>
<Link href="/">Bosh sahifa</Link>{" "}
<Link href="/blog">Blog</Link>
</nav>
<p>Manzil: {pathname}</p>
{findPage(pathname)}
</>
);
}Ekranda:
Bosh sahifa Blog
Manzil: /blog/salom
Maqola: salomStatik FILES avval tekshiriladi — «Qaysi papka yutadi?» bo'limidagi qoida: aniq nom dinamikdan kuchli. /blog/arxiv ni FILES ga qo'shsangiz, u BlogPost ga tushmaydi.
3-mashq (qiyin): qo'llanma uchun "non yo'li"
app/qollanma/[[...qism]]/page.tsx uchun yordamchi funksiya yozing: breadcrumb(qism) — params.qism ni oladi va "Qo'llanma › next › salom dunyo" kabi satr qaytaradi. Shartlar:
qism—undefinedbo'lishi mumkin (/qollanma): natija"Qo'llanma".- Segmentlar dekodlanmagan keladi (
"salom%20dunyo") —decodeURIComponentdan o'tkazing. - Buzuq kodlash (
"%E0")decodeURIComponentdaURIErrortashlaydi — bunday segmentni o'zgartirmasdan qoldiring (try/catch— xatolarni ushlash).
Sinov: breadcrumb(undefined), breadcrumb(["next", "salom%20dunyo"]), breadcrumb(["%E0"]).
Yechim
function decodeSafe(segment) {
try {
return decodeURIComponent(segment);
} catch {
return segment; // buzuq kodlash — o'zgarishsiz
}
}
function breadcrumb(qism) {
const parts = (qism ?? []).map(decodeSafe);
return ["Qo'llanma", ...parts].join(" › ");
}
console.log(breadcrumb(undefined));
console.log(breadcrumb(["next", "salom%20dunyo"]));
console.log(breadcrumb(["%E0"]));Konsolda:
Qo'llanma
Qo'llanma › next › salom dunyo
Qo'llanma › %E0?? [] — undefined ni bo'sh massivga aylantiradi, shunda map xatosiz ishlaydi. catch qavssiz (catch {) — xato obyekti kerak bo'lmasa shunday yoziladi.
4-mashq: Portfolio qadami
O'z portfolio ingizga v1 sahifalarini ko'chiring: app/page.tsx, haqimda/, loyihalar/, loyihalar/[slug]/, aloqa/. Loyihalar — src/malumot/loyihalar.ts massivida (kamida 3 ta, birinchisi — vazifalar, case study bilan). Matnlarni v1 dan oling — o'z ismingiz, o'z loyihalaringiz. [slug] sahifasida await params va generateStaticParams. Tekshiring: npm run tip va npm run build toza, build jadvalida har loyiha ○.
Yechim
Namuna — «Portfolio qadami» bo'limidagi tuzilma va loyiha sahifasi. Build jadvalingizda ◐ /loyihalar/[slug] va har loyihangiz uchun bitta ○ qator bo'lishi kerak. /loyihalar/yoq hali 200 qaytaradi — bu kutilgan, uni maxsus fayllar darsida tuzatamiz. Commit hali qilmang: keyingi darsda layout bilan birga bitta commit bo'ladi.
10. Real ishda
- Har Next.js loyihasi shu tuzilmada: intervyuda "
app/shop/[...slug]/page.tsxqaysi manzillarni ochadi?" kabi savollar tez-tez beriladi. - Do'konlar kategoriya daraxti uchun
[...kategoriya], hujjat saytlari (Next.js'ning o'z hujjatlari ham)[[...slug]]ishlatadi: bitta sahifa fayli minglab sahifaga xizmat qiladi. - Next 14 → 15/16 ko'chishi — haqiqiy loyihalarda eng ko'p ish talab qilgan o'zgarishlardan biri aynan
paramsning Promise bo'lishi. Next.js buning uchun avtomatik tuzatuvchi (codemod) chiqargan — Versiyalarni yangilash darsida. PagePropsva typegen — havola va yo'l xatolarini build'dan oldin ushlaydi. Katta jamoalarda "sahifa nomini o'zgartirdim, 40 ta havola buzildi" muammosining oldini oladi.
Xulosa
app/ichidagi papka — URL segmenti,page.tsx— shu segmentning sahifasi;page.tsxsiz papka ochilmaydi.page.tsxdaexport defaultmajburiy; funksiya nomi manzilga ta'sir qilmaydi.[slug]— bitta segment (satr),[...qism]— kamida bitta (massiv),[[...qism]]— nol yoki ko'p (bo'shda{}).params— Promise:const { slug } = await params;PageProps<"/yo'l">turi typegen'dan keladi.- Aniq nom dinamikdan kuchli; bir darajada ikki xil dinamik ism — build xatosi.
- Cache Components'da dinamik sahifa
generateStaticParamsso'raydi; noma'lum slug hozircha 200 qaytaradi.
Keyingi dars: Layout va template — header va footer har sahifada takrorlanmasligi uchun umumiy qobiq, va nega template har o'tishda qayta yaratiladi.
Manbalar
- Next.js hujjati — Project structure va Dynamic Segments: https://nextjs.org/docs/app/getting-started/project-structure , https://nextjs.org/docs/app/api-reference/file-conventions/dynamic-routes (2026-10-21 da o'qildi)
- Next.js hujjati —
page.jsvaPageProps: https://nextjs.org/docs/app/api-reference/file-conventions/page - Next.js 15 —
paramsPromise'ga o'tishi: https://nextjs.org/blog/next-15 - Kanon o'lchovlari:
portfoliov2, holat 04 (next 16.4.0, Node 24.21.0, Chrome 155); tajribalar — biz ishga tushirdik (next 16.4.0,next build+next start).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!