Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Root layout
- 2.1 children — sahifa tushadigan joy
- 2.2 <html> va <body> — faqat root layout'da
- 3. Ichma-ich layout'lar
- 3.1 Har papka — o'z qobig'i
- 3.2 Layout'ga ham params keladi
- 4. Layout saqlanadi, sahifa almashadi
- 4.1 Nima saqlanadi?
- 4.2 Template — har safar yangi
- 4.3 Natija oynasida: layout va template modeli
- 4.4 Haqiqiy Next.js'da ham shundaymi?
- 5. Layout'ga searchParams kelmaydi
- 5.1 Haqiqiy tur xatosi
- 5.2 Nega?
- 5.3 Yechim: kichik interaktiv komponent
- 6. Template: qachon kerak?
- 6.1 portfolio dagi template
- 6.2 Template'ning boshqa ishlari
- 7. Biz o'lchadik: animatsiyaning narxi
- 8. Portfolio qadami: umumiy qobiq va commit
- 8.1 Fayllar
- 8.2 Tekshiruv va commit
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): layout yoki template?
- 2-mashq (o'rta): «Loyihalar» bo'limi layout'i
- 3-mashq (qiyin): sahifa ko'rishlar hisobi
- 4-mashq: Portfolio qadami
- 11. Real ishda
- Xulosa
- Manbalar
Next.js layout va template: umumiy qobiq, ichma-ich layout va template.tsx
Qisqacha: Layout (
layout.tsx) — bir nechta sahifaga umumiy qobiq: header, menyu, footer. Uchildrenni oladi va sahifa shu joyga tushadi. Sahifadan sahifaga o'tganda layout qayta yaratilmaydi: uning ichidagi state (yozilgan matn, ochiq menyu) saqlanadi. Eng tepadagi root layout majburiy va<html>,<body>ni o'zi chizadi. Template (template.tsx) ham qobiq, lekin u har o'tishda noldan yaratiladi — kirish animatsiyasi yoki har sahifada qayta boshlanadigan narsa uchun. Layout'gasearchParamskelmaydi: u har o'tishda qayta chizilmaydi, demak eskirgan qiymat ko'rsatib qolardi.
Bu darsda
- Root layout yozasiz va unda nega
<html>va<body>shartligini haqiqiy xato bilan ko'rasiz. - Ichma-ich layout'lar qanday ustma-ust o'ralishini chizib bera olasiz.
- Layout'dagi state nega saqlanishini, template'dagi esa nega yo'qolishini tushuntirasiz (haqiqiy brauzer tajribasi bilan).
- Layout'ga nega
searchParamsva joriy manzil kelmasligini va o'rniga nima qilinishini bilasiz. portfolioga umumiy header, footer va template kirish animatsiyasini qo'shasiz — va bu animatsiyaning narxini o'lchangan raqam bilan ko'rasiz.
Oldin bilishingiz kerak: Fayl-asosli routing, React Router II: layout marshrutlar, State'ning saqlanishi va key bilan reset, @keyframes va animation, prefers-reduced-motion.
1. Nega bu kerak?
Oldingi darsda portfolio ga beshta sahifa ko'chdi. Lekin ularda menyu ham, footer ham yo'q. Har page.tsx ga header nusxasini qo'yamizmi?
04-qismda aynan shunday qilgandingiz. v1 ning beshta HTML faylida bir xil <header> va <footer> turardi. Menyuga «Blog» qo'shish — beshta faylni tahrirlash edi. Bittasini unutsangiz, o'sha sahifada menyu boshqacha qolardi.
React Router bu muammoni layout marshrut va <Outlet /> bilan hal qildi. Next.js'da g'oya o'sha, faqat yana fayl bilan: papkaga layout.tsx qo'yasiz, va shu papkadagi hamma sahifa uning ichida chiziladi.
Hayotiy o'xshatish: avtobus. Avtobus — layout: haydovchi, o'rindiqlar, chiptachi. Bekatlar — sahifalar. Bekatdan bekatga o'tganda avtobus almashmaydi, yo'lovchilar o'z joyida o'tiraveradi. Template esa har bekatda yangi beriladigan chiptaga o'xshaydi: avtobus o'sha, chipta har safar yangi.
2. Root layout
2.1 children — sahifa tushadigan joy
app/layout.tsx — root layout: butun ilovaning eng tashqi qobig'i. create-next-app uni o'zi yaratgan. portfolio dagi to'liq holati:
import type { Metadata } from "next";
import { SaytPastki } from "@/komponentlar/SaytPastki";
import { SaytSarlavhasi } from "@/komponentlar/SaytSarlavhasi";
import "./globals.css";
export const metadata: Metadata = {
title: "Aziz Karimov — portfolio",
description: "Aziz Karimovning portfoliosi: loyihalar, blog va aloqa.",
};
// Root layout — barcha sahifalar uchun umumiy qobiq (N-04).
// Navigatsiyada qayta render bo'lmaydi.
export default function RootLayout({ children }: LayoutProps<"/">) {
return (
<html lang="uz">
<body>
<a className="otish" href="#asosiy">
Asosiy kontentga o'tish
</a>
<SaytSarlavhasi />
{children}
<SaytPastki />
</body>
</html>
);
}Qatorma-qator:
| Qator | Nima qiladi |
|---|---|
import "./globals.css" |
Butun sayt CSS'i — bir marta, shu yerda |
metadata |
Sukut bo'yicha <title> va description; sahifa o'zinikini bersa — o'shanisi |
LayoutProps<"/"> |
Global tur (typegen), PageProps ning layout'dagi juftligi |
<html lang="uz"> |
Sahifa tili — ekran o'quvchi va Google uchun |
<a className="otish"> |
«Asosiy kontentga o'tish» havolasi — v1 dagi skip link |
{children} |
Joriy sahifa (page.tsx) shu yerga tushadi |
{children} — React Router'dagi <Outlet /> ning o'rni. Farqi: Outlet'ni router qo'yadi, children esa oddiy React prop (children va kompozitsiya darsidagi children ning aynan o'zi).
Izohdagi (N-04) — kanon kodidagi dars belgisi. Uzun izoh telefonga sig'ishi uchun ikki qatorga bo'lindi.
2.2 <html> va <body> — faqat root layout'da
Oddiy React ilovada (Vite) <html> va <body> index.html faylida turardi, React esa <div id="root"> ichiga chizardi. Next.js'da index.html yo'q. Butun sahifani — <html> dan boshlab — React chizadi. Shuning uchun bu teglarni root layout yozishi shart.
Biz tajriba qildik: root layout'dan <html> va <body> ni olib tashladik (o'rniga <>…</>). Natija ikki xil chiqdi.
next dev da brauzerdagi xato oynasi:
Runtime Error
Missing <html> and <body> tags in the root layout.
Read more at https://nextjs.org/docs/messages/missing-root-layout-tagsTarjimasi: "Root layout'da <html> va <body> teglari yo'q".
next build esa — xatosiz o'tdi. Build jadvali odatdagidek. Production'da (next start) sahifa ochiladi, lekin Chrome'da tekshirsak:
compatMode: BackCompatBackCompat — brauzerning quirks mode (eski saytlarga moslashish rejimi). Sahifa boshida <!DOCTYPE html> yo'q, lang="uz" ham yo'q. CSS o'lchamlari eski qoidalar bilan hisoblanadi, ekran o'quvchi sahifa tilini bilmaydi. Xato jim: faqat dev'da ko'rinadi. Saboq — dev'dagi qizil oynani "keyin tuzataman" deb yopmang.
Tekshirib ko'ring:
app/loyihalar/layout.tsxichida ham<html>yozsak nima bo'ladi?
Javob
Ikkita <html> bo'lib qoladi: ichma-ich layout root layout'ning ichida chiziladi. <html> va <body> — faqat root layout'da. Ichki layout'lar oddiy elementlar qaytaradi (<section>, <div>, <>…</>).
3. Ichma-ich layout'lar
3.1 Har papka — o'z qobig'i
Layout faqat ildizda emas, istalgan papkada bo'lishi mumkin. Faraz qiling, «Loyihalar» bo'limida chap tomonda loyihalar ro'yxati turishi kerak — ro'yxat sahifasida ham, har loyiha sahifasida ham:
// app/loyihalar/layout.tsx
export default function LoyihalarLayout({
children,
}: LayoutProps<"/loyihalar">) {
return (
<div className="ikki-ustun">
<aside>{/* loyihalar ro'yxati */}</aside>
{children}
</div>
);
}Endi /loyihalar/bahor ochilganda Next.js uchta komponentni ichma-ich qo'yadi:
flowchart TB
R["app/layout.tsx<br/>html, body, header, footer"] --> L["app/loyihalar/layout.tsx<br/>aside + children"]
L --> P["app/loyihalar/[slug]/page.tsx<br/>«Bahor» sahifasi"]JSX'da yozsak, aslida shunday chiziladi:
<RootLayout>
<LoyihalarLayout>
<LoyihaSahifasi params={…} />
</LoyihalarLayout>
</RootLayout>/haqimda ochilganda esa LoyihalarLayout umuman qatnashmaydi — u faqat loyihalar/ papkasi va uning ichidagi sahifalarni o'raydi. Qoida: sahifadan ildizgacha yo'ldagi hamma layout ustma-ust o'raladi.
portfolio kanonida hozircha ichki layout yo'q. U Parallel va intercepting routes darsida paydo bo'ladi: loyihalar/layout.tsx loyiha rasmini modal oynada ko'rsatish uchun. Header va footer esa root layout'da qoladi — sababini Route groups darsida ko'rasiz.
3.2 Layout'ga ham params keladi
Dinamik papkadagi layout o'z segmentining qiymatini oladi — page.tsx dagidek, Promise sifatida. Masalan app/loyihalar/[slug]/layout.tsx da LayoutProps<"/loyihalar/[slug]"> → params: Promise<{ slug: string }>. Ota layout esa bola segmentning params ini olmaydi: app/loyihalar/layout.tsx slug ni bilmaydi, chunki u /loyihalar ro'yxat sahifasini ham o'raydi — u yerda slug umuman yo'q.
4. Layout saqlanadi, sahifa almashadi
4.1 Nima saqlanadi?
Sahifadan sahifaga <Link> bilan o'tganda Next.js faqat o'zgargan qismni yangilaydi. / dan /loyihalar ga o'tsangiz, root layout umumiy — u joyida qoladi, faqat {children} almashadi. Bu qisman render (partial rendering) deyiladi.
Natijasi — layout ichidagi hamma narsa tirik qoladi:
- inputga yozilgan matn (masalan header'dagi qidiruv maydoni);
useStateqiymatlari (ochiq menyu, hisoblagich);- ijro etilayotgan audio yoki video;
- scroll holati (layout'ning o'z scroll qismi bo'lsa).
State'ning saqlanishi darsidagi qoidani eslang: React komponentni daraxtdagi joyi va turi o'zgarmaguncha saqlaydi. Root layout har sahifada daraxtning tepasida, bir xil komponent — React uni hech qachon qayta yaratmaydi.
4.2 Template — har safar yangi
Template (template.tsx) — layout'ga o'xshash qobiq, bitta farq bilan: har o'tishda React uni yangidan yaratadi. Ichidagi state nolga qaytadi, effektlar qayta ishga tushadi.
Next.js buni qanday qiladi? Template'ga segment nomidan key beradi. State'ning saqlanishi darsidan bilasiz: key o'zgarsa, React eski komponentni o'chirib, yangisini yaratadi. Template — shu hiylaning fayl ko'rinishi.
Joyi — layout bilan sahifa orasida:
flowchart TB
L["layout.tsx — saqlanadi"] --> T["template.tsx — har o'tishda yangi"]
T --> P["page.tsx — joriy sahifa"]4.3 Natija oynasida: layout va template modeli
Natija oynasida Next.js serveri yo'q (sababi oldingi darsda aytilgan: next/link — o'quv modeli). Lekin layout va template'ning xulqini oddiy React bilan aniq takrorlash mumkin, chunki Next.js ham aynan React'ning shu qoidalariga tayanadi. RootLayout — oddiy o'rovchi komponent, Template — key={pathname} bilan o'ralgan komponent.
Ikkalasida bittadan hisoblagich bor. «+1» tugmalarini bosing, keyin boshqa sahifaga o'ting:
import { useEffect, useState, type ReactNode } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
function Counter({ place }: { place: string }) {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`${place}: yaratildi`);
}, [place]);
return (
<button
aria-label={`${place} +1`}
onClick={() => setCount(count + 1)}
>
{place}: {count}
</button>
);
}
function RootLayout({ children }: { children: ReactNode }) {
return (
<>
<nav>
<Link href="/">Bosh sahifa</Link>{" "}
<Link href="/loyihalar">Loyihalar</Link>
</nav>
<Counter place="layout" />
{children}
</>
);
}
function Template({ children }: { children: ReactNode }) {
return (
<div>
<Counter place="template" />
{children}
</div>
);
}
const PAGES: Record<string, string> = {
"/": "Bosh sahifa",
"/loyihalar": "Loyihalar",
};
export default function App() {
const pathname = usePathname();
return (
<RootLayout>
{/* Next.js template'ni shunday o'raydi: key — segment */}
<Template key={pathname}>
<h1>{PAGES[pathname]}</h1>
</Template>
</RootLayout>
);
}Konsolda:
layout: yaratildi
template: yaratildi
template: yaratildiEkranda:
Bosh sahifa Loyihalar
layout: 2
template: 0
LoyihalarNima bo'ldi, qadamma-qadam:
| Qadam | layout | template | Konsol |
|---|---|---|---|
| ochildi | 0 | 0 | ikkalasi "yaratildi" |
| 2 + 1 bosish | 2 | 1 | — |
| «Loyihalar» | 2 (saqlandi) | 0 (yangi) | faqat "template: yaratildi" |
Layout hisoblagichi 2 da qoldi — komponent o'sha. Template hisoblagichi 0 ga qaytdi va effekt qayta ishladi — komponent yangi.
4.4 Haqiqiy Next.js'da ham shundaymi?
Model — model. Shuning uchun biz xuddi shu tajribani haqiqiy portfolio (holat 04) da qildik. Root layout'ga va template.tsx ga bittadan client hisoblagich qo'ydik, next build + next start, keyin headless Chrome'da: layout'ni 2 marta, template'ni 1 marta bosdik va menyudan o'tdik.
[konsol] layout: yaratildi
[konsol] template: yaratildi
ochildi: layout 0 | template 0 | / | navigation=1
3 bosish: layout 2 | template 1 | / | navigation=1
[konsol] template: yaratildi
Loyihalar: layout 2 | template 0 | /loyihalar | navigation=1
[konsol] template: yaratildi
Men haqimda: layout 2 | template 0 | /haqimda | navigation=1
chrome Chrome/155.0.8059.85Natija model bilan bir xil: layout 2 da qoldi, template har o'tishda 0 dan boshladi. navigation=1 — brauzer sahifani bir marta yuklagan, keyingi o'tishlar to'liq qayta yuklashsiz. Client hisoblagich uchun fayl boshida "use client" qatori kerak bo'ldi — bu nima ekanini Client Components darsida ochamiz.
Tekshirib ko'ring: header'da qidiruv maydoni bor. Foydalanuvchi "react" deb yozdi va «Loyihalar» ga o'tdi. Maydon root layout'da bo'lsa matn qoladimi?
template.tsxda bo'lsa-chi?
Javob
Layout'da — qoladi: komponent qayta yaratilmaydi, input state'i tirik. Template'da — yo'qoladi: template har o'tishda yangi key bilan noldan yaratiladi. Qidiruv kabi narsalar — layout'ga.
5. Layout'ga searchParams kelmaydi
5.1 Haqiqiy tur xatosi
Oldingi darsda sahifa params olishini ko'rdik. Sahifaga yana searchParams ham keladi — ?teg=React kabi query qismi (Navigatsiya darsida). Layout'ga-chi? Sinab ko'rdik:
export default async function LoyihalarLayout({
children,
searchParams,
}: LayoutProps<"/loyihalar">) {
return <section>{children}</section>;
}npm run tip:
src/app/loyihalar/layout.tsx(3,3): error TS2339: Property 'searchParams' does not exist on type 'LayoutProps<"/loyihalar">'.Tarjimasi: "LayoutProps<"/loyihalar"> turida searchParams xususiyati yo'q". Bu tasodif emas, ataylab qilingan.
5.2 Nega?
Layout har o'tishda qayta chizilmaydi («Layout saqlanadi, sahifa almashadi» bo'limi). /loyihalar?teg=React dan /loyihalar?teg=CSS ga o'tdingiz — sahifa yangilanadi, layout esa joyida. Agar layout searchParams olganda, u eski teg=React ni ko'rsatib qolardi. Next.js bunday yolg'onga yo'l qo'ymaydi: layout'ga faqat o'zgarmaydigan narsa — children va o'z params i keladi.
Xuddi shu sababdan server layout joriy manzilni (pathname) ham bilmaydi. Menyuda "hozir qaysi sahifadasiz" belgisi (joriy havola) layout'da serverda chizilsa, birinchi sahifadagi belgi abadiy qolardi.
5.3 Yechim: kichik interaktiv komponent
Manzilni brauzerda o'qiydigan kichik komponent layout ichiga qo'yiladi. U usePathname() bilan joriy yo'lni oladi va manzil o'zgarganda o'zi qayta chiziladi — layout esa qayta chizilmaydi. portfolio da bu NavHavola komponenti, Navigatsiya darsida qo'shiladi. Natija oynasida g'oyasi:
import Link from "next/link";
import { usePathname } from "next/navigation";
function NavLink({ href, label }: { href: string; label: string }) {
const pathname = usePathname();
const active = pathname === href;
return (
<Link href={href} aria-current={active ? "page" : undefined}>
{active ? `[${label}]` : label}
</Link>
);
}
// Layout o'zi manzilni bilmaydi — faqat NavLink biladi
function Header() {
console.log("Header chizildi");
return (
<nav>
<NavLink href="/" label="Bosh sahifa" />{" "}
<NavLink href="/loyihalar" label="Loyihalar" />
</nav>
);
}
export default function App() {
return <Header />;
}Konsolda:
Header chizildiEkranda:
Bosh sahifa [Loyihalar]Kvadrat qavs — joriy sahifa belgisi (haqiqiy saytda bu CSS bilan chiziladi, aria-current="page" ekran o'quvchiga "joriy sahifa" deydi). «Loyihalar» bosilganda belgi ko'chdi, lekin konsolda "Header chizildi" bir marta: Header qayta chizilmadi, faqat ikkita NavLink yangilandi. Layout ham aynan shunday ishlashi kerak — o'zgaruvchan qismni kichik komponentga ajrating.
6. Template: qachon kerak?
6.1 portfolio dagi template
Kanondagi template.tsx:
// template — layout'dan farqli, har navigatsiyada QAYTA
// yaratiladi (N-04): kirish animatsiyasi har sahifada boshlanadi.
export default function Template({
children,
}: {
children: React.ReactNode;
}) {
return <div className="sahifa">{children}</div>;
}Kanon kodida funksiya imzosi bitta qator (prettier printWidth 80); bu yerda telefonga sig'ishi uchun bo'lindi. LayoutProps o'rnida oddiy tur — template'ga faqat children keladi.
globals.css dagi juftligi:
@layer komponentlar {
.sahifa {
display: contents;
}
@media (prefers-reduced-motion: no-preference) {
.sahifa > main {
animation: sahifa-kirish 200ms ease-out;
}
}
@keyframes sahifa-kirish {
from {
opacity: 0;
translate: 0 0.5rem;
}
}
}Uchta detal:
display: contents—<div className="sahifa">o'zi quti chizmaydi, layout'ga xalaqit bermaydi; bolalari go'yo to'g'ridan root layout ichida turgandek joylashadi.- Animatsiya faqat
prefers-reduced-motion: no-preferenceda — harakatdan boshi aylanadigan foydalanuvchi uchun yoqilmaydi (prefers-reduced-motiondarsidagi qoida). @keyframesda faqatfrom—toyozilmasa, brauzer elementning odatdagi holatini oladi.
Template har o'tishda yangi bo'lgani uchun <main> ham yangi element — animatsiya har sahifada qaytadan boshlanadi. Layout'ga qo'yilsa, faqat birinchi ochilishda bir marta o'ynardi.
6.2 Template'ning boshqa ishlari
| Vazifa | Nega template |
|---|---|
| Kirish animatsiyasi | Har sahifada element yangidan yaratiladi |
| Har sahifa ochilishini yozish (analitika) | useEffect har o'tishda qayta ishlaydi |
| Sahifaga xos forma yoki holatni tozalash | State nolga qaytadi |
| Har o'tishda Suspense fallback'ini ko'rsatish | Yangi Suspense chegarasi (Streaming) |
Lekin ko'p hollarda template kerak emas. Next.js hujjati ham avval layout'ni tavsiya qiladi. Template — "har safar noldan" ataylab kerak bo'lganda.
Tekshirib ko'ring:
app/da hamlayout.tsx, hamtemplate.tsxbor./aloqaochilganda ular qaysi tartibda o'raladi?
Javob
RootLayout → Template → AloqaSahifasi. Template layout'ning {children} o'rnida turadi va o'zi sahifani o'raydi. Kanondagi HTML'da ham shunday: body > div.sahifa > main#asosiy.
7. Biz o'lchadik: animatsiyaning narxi
200 ms lik yengil kirish animatsiyasi — zararsizdek. Kanon portfolio ni oxirigacha qurgach, Performance bosqichida Lighthouse (mobil) bilan o'lchadi. Bosh sahifaning eng katta elementi (LCP, Largest Contentful Paint — ekrandagi eng katta matn yoki rasm chizilgan vaqt; Core Web Vitals) — kirish paragrafi. Lighthouse LCP'ni qismlarga ajratdi:
- template animatsiyasi bilanholat 37345 ms
- animatsiyasiztajriba132 ms
Manba: IlmHamroh kanoni, Lighthouse 13.5.0 mobil, Chrome 155, 2026-10-21
"Element render delay" (elementni chizish kechikishi) 345 ms dan 132 ms ga tushdi. Sabab oddiy: paragraf opacity: 0 dan boshlanadi. Brauzer uchun shaffof matn hali "chizilmagan" — LCP animatsiya tugashini kutadi.
Lighthouse natijasi (bosh sahifa, mobil):
| Performance | LCP | |
|---|---|---|
| animatsiya bilan | 94 | 3.1 s |
| animatsiyasiz | 97 | 2.6 s |
Hamma o'lchangan sahifalarda shunday: animatsiya bilan Performance 93–96, LCP 2.7–3.2 s. Kanon qaror qildi: holat 38 da template.tsx va animatsiya olib tashlandi. Bu darsda (holat 04) esa template bor — o'quvchi uni ko'rsin, keyin narxini o'lchab, o'zi tanlasin.
Saboq ikki qavatli. Birinchisi: chiroyli effektning ham narxi bor, va u faqat o'lchanganda ko'rinadi. Ikkinchisi: narx har doim "olib tashla" degani emas. Animatsiyani <main> ga emas, LCP bo'lmaydigan elementga (masalan pastki bo'limlarga) qo'yish yoki faqat translate bilan (opacity siz) qilish — LCP'ni kechiktirmaydi. Buni Performance darsida sinab ko'ramiz.
8. Portfolio qadami: umumiy qobiq va commit
8.1 Fayllar
Oldingi darsdagi sahifalarga qo'shiladi:
| Fayl | Nima |
|---|---|
app/layout.tsx |
lang="uz", skip link .otish, SaytSarlavhasi, {children}, SaytPastki |
app/template.tsx |
<div className="sahifa"> — kirish animatsiyasi |
app/globals.css |
v1 tokenlari, qatlamlar @layer tokenlar, reset, asos, layout, komponentlar |
app/icon.svg |
v1 favicon — fayl nomi o'zi <link rel="icon"> ni qo'shadi |
komponentlar/SaytSarlavhasi.tsx |
Ism + menyu: 4 ta <Link> |
komponentlar/SaytPastki.tsx |
v1 footer: email, Telegram, © |
Menyu — massivdan:
const MENYU = [
{ href: "/", nomi: "Bosh sahifa" },
{ href: "/haqimda", nomi: "Men haqimda" },
{ href: "/loyihalar", nomi: "Loyihalar" },
{ href: "/aloqa", nomi: "Aloqa" },
] as const;Endi menyuga «Blog» qo'shish — bitta qator, bitta fayl. v1 dagi beshta fayl o'rniga.
globals.css — v1 ning CSS'i (05-qism oklch palitrasi, qorong'i palitra, 06-qism qatlamlari) bitta faylda. Shrift hozircha tizim shrifti; v1 dagi Rubik next/font darsida qaytadi.
icon.svg — Next.js'ning fayl konvensiyasi: app/ ildiziga shu nom bilan qo'yilgan rasm favicon bo'ladi, HTML'ga hech narsa yozmaysiz. Skeletdagi favicon.ico 404 xatosi shu bilan yopildi.
8.2 Tekshiruv va commit
Lint, tip, format — 0. Build — 8 s, jadval oldingi darsdagidek (○ 5 sahifa + 3 loyiha, ◐ /loyihalar/[slug]). Kanon o'lchovi: axe 0 buzilish (/yoq dagi standart 404 dan tashqari), hydration ogohlantirishi 0, CSS 1.8 KB, JS 135.4 KB gzip — layout va template JS hajmini oshirmadi (ular serverda chiziladi).
git add -A
git commitCommit (kanon): Sahifalar: bosh, haqimda, loyihalar, loyihalar/[slug], aloqa; umumiy layout va template.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Dev'da Missing <html> and <body> tags in the root layout. |
Root layout <html>/<body> siz |
Ikkalasini root layout'da yozing |
| Build o'tdi, sahifa quirks mode'da | Xuddi shu — production'da jim | Dev xatosini e'tiborsiz qoldirmang |
Sahifada ikki <html> (noto'g'ri tuzilma) |
Ichki layout'da ham <html> |
Faqat root layout'da |
Property 'searchParams' does not exist on type 'LayoutProps<…>' |
Layout'da searchParams |
Sahifada o'qing yoki client komponentda useSearchParams |
| Menyudagi joriy belgi yangilanmaydi | Joriy manzil layout'da hisoblangan | usePathname bilan kichik komponent |
| Input matni har o'tishda yo'qoladi | Input template.tsx da |
Layout'ga ko'chiring |
| Animatsiya faqat birinchi marta | Animatsiya layout'dagi elementda | Template ichidagi elementga |
| LCP kechikdi | opacity: 0 dan boshlanadigan kirish animatsiyasi |
O'lchang; LCP elementidan olib tashlang |
10. Mashqlar
Mashqlar kurs/mashqlar/23/04-layout-template/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): layout yoki template?
- Hamma sahifada bir xil header —
- Har sahifa ochilganda "sahifa ko'rildi" yozuvi —
- Musiqa pleyeri sahifadan sahifaga o'tganda to'xtamasin —
<html lang="uz">qayerda yoziladi — root- Har sahifada qayta boshlanadigan kirish animatsiyasi —
Yechim
- layout — umumiy, o'zgarmaydigan qobiq.
- template — effekt har o'tishda qayta ishlashi kerak.
- layout — pleyer state'i (qaysi qo'shiq, qayerda) saqlanishi uchun komponent qayta yaratilmasligi kerak.
- layout — faqat root layout; template
<html>chiza olmaydi. - template — yangi element = yangi animatsiya.
2-mashq (o'rta): «Loyihalar» bo'limi layout'i
«Layout va template modeli» blokini oling. Template'ni olib tashlang va /loyihalar bo'limi uchun ichki layout qo'shing: ProjectsLayout — ichida o'z Counter i (place="loyihalar"). Sahifalar: /, /loyihalar, /loyihalar/bahor (har birida havola bo'lsin). ProjectsLayout faqat /loyihalar bilan boshlanadigan manzillarda chizilsin (pathname.startsWith("/loyihalar") — satr metodlari). Tekshiring: /loyihalar da «+1» ni bosib, /loyihalar/bahor ga o'tsangiz — hisob qoladimi? Bosh sahifaga qaytsangiz-chi?
Yechim
import { useEffect, useState, type ReactNode } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
function Counter({ place }: { place: string }) {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`${place}: yaratildi`);
return () => console.log(`${place}: o'chirildi`);
}, [place]);
return (
<button
aria-label={`${place} +1`}
onClick={() => setCount(count + 1)}
>
{place}: {count}
</button>
);
}
// app/loyihalar/layout.tsx o'rnida
function ProjectsLayout({ children }: { children: ReactNode }) {
return (
<section>
<Counter place="loyihalar" />
{children}
</section>
);
}
const PAGES: Record<string, string> = {
"/": "Bosh sahifa",
"/loyihalar": "Loyihalar",
"/loyihalar/bahor": "Bahor",
};
export default function App() {
const pathname = usePathname();
const page = <h1>{PAGES[pathname]}</h1>;
return (
<>
<nav>
<Link href="/">Bosh sahifa</Link>{" "}
<Link href="/loyihalar">Loyihalar</Link>{" "}
<Link href="/loyihalar/bahor">Bahor</Link>
</nav>
{pathname.startsWith("/loyihalar") ? (
<ProjectsLayout>{page}</ProjectsLayout>
) : (
page
)}
</>
);
}Konsolda:
loyihalar: yaratildi
loyihalar: o'chirildiEkranda:
Bosh sahifa Loyihalar Bahor
Bosh sahifa/loyihalar → /loyihalar/bahor: ProjectsLayout joyida qoldi, hisob 1 da saqlandi (ekran("loyihalar: 1") qadami buni tekshiradi). Bosh sahifaga qaytganda layout daraxtdan chiqdi — "o'chirildi". Keyingi safar /loyihalar ga kirsangiz, u 0 dan boshlaydi. Haqiqiy Next.js'da ham ichki layout faqat o'z papkasi ichida yashaydi.
3-mashq (qiyin): sahifa ko'rishlar hisobi
Har sahifa ochilganda konsolga ko'rildi: /yo'l yozilsin — analitika uchun. Ikki variant yozing: LayoutTracker (layout ichida, usePathname siz, faqat useEffect(…, [])) va TemplateTracker (template ichida, key={pathname} bilan). Uch sahifani ketma-ket oching (/ → /haqimda → /aloqa) va qaysi variant uchta yozuv berishini ko'rsating. Har yozuvga [layout] yoki [template] belgisi qo'ying. Joriy yo'lni effekt ichida usePathname() dan oling.
Yechim
import { useEffect, type ReactNode } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
function LayoutTracker() {
const pathname = usePathname();
useEffect(() => {
console.log(`[layout] ko'rildi: ${pathname}`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
}
function TemplateTracker() {
const pathname = usePathname();
useEffect(() => {
console.log(`[template] ko'rildi: ${pathname}`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return null;
}
function Template({ children }: { children: ReactNode }) {
return (
<>
<TemplateTracker />
{children}
</>
);
}
export default function App() {
const pathname = usePathname();
return (
<>
<LayoutTracker />
<nav>
<Link href="/haqimda">Men haqimda</Link>{" "}
<Link href="/aloqa">Aloqa</Link>
</nav>
<Template key={pathname}>
<p>Sahifa: {pathname}</p>
</Template>
</>
);
}Konsolda:
[layout] ko'rildi: /
[template] ko'rildi: /
[template] ko'rildi: /haqimda
[template] ko'rildi: /aloqaEkranda:
Men haqimda Aloqa
Sahifa: /aloqaLayout varianti faqat bir marta yozdi — u qayta yaratilmaydi, bo'sh [] li effekt ham qayta ishlamaydi. Template varianti har o'tishda yozdi. Eslatma: haqiqiy loyihada analitikani layout'da useEffect(…, [pathname]) bilan ham qilsa bo'ladi — bu ham to'g'ri yo'l. Mashqning maqsadi — ikki komponentning hayot sikli farqini ko'rish. eslint-disable qatori ataylab: ESLint pathname ni bog'liqlikka qo'shishni so'raydi, biz esa "faqat yaratilganda" xulqini ko'rsatyapmiz.
4-mashq: Portfolio qadami
O'z portfolio ingizga root layout (skip link, header, footer), template.tsx + kirish animatsiyasi (prefers-reduced-motion ichida) va icon.svg ni qo'shing. Menyu — massivdan. next dev da sahifalar orasida o'tib ko'ring: animatsiya har sahifada qayta o'ynashi kerak. Keyin npm run lint, npm run tip, npm run build — toza bo'lsa, commit.
Yechim
Namuna — «Portfolio qadami» bo'limidagi fayllar jadvali va root layout kodi. Tekshirish uchun: DevTools'da (Rendering paneli) "Emulate CSS prefers-reduced-motion: reduce" ni yoqing — animatsiya yo'qolishi kerak. Commit xabarini o'zingiz yozing yoki kanondagini oling. Animatsiyaning LCP narxini hozir o'lchamang — Performance darsida Lighthouse bilan birga qilamiz.
11. Real ishda
- Har Next.js loyihasida root layout bor: shrift, analitika skripti, tema provayderi (Provider'lar), umumiy header — hammasi shu yerga.
- Admin panellar — klassik ichma-ich layout:
app/admin/layout.tsxda yon menyu, ichidagi sahifalar almashadi, menyu va uning scroll holati saqlanadi. - Template amalda kam uchraydi: animatsiya kutubxonalari (Motion) bilan sahifa o'tishlari, sahifa ko'rishlarini yozish. Intervyuda "layout va template farqi nima?" — tez-tez beriladigan savol: javob — qayta yaratilish va
key. - "Layout'da
searchParamsyo'q" — tajribasiz dasturchilarning eng ko'p savollaridan biri. Endi sababini bilasiz.
Xulosa
layout.tsx— papkadagi hamma sahifaga umumiy qobiq; sahifa{children}o'rniga tushadi.- Root layout majburiy va
<html>,<body>ni o'zi yozadi; bo'lmasa dev xato beradi, build esa jim quirks mode sahifa chiqaradi. - Ichma-ich layout'lar sahifadan ildizgacha ustma-ust o'raladi; o'tishda umumiy layout saqlanadi, state tirik qoladi.
template.tsxhar o'tishdakeybilan yangidan yaratiladi — animatsiya va "har safar" effektlari uchun.- Layout'ga
searchParamsva joriy manzil kelmaydi; ularni kichik client komponent (usePathname) o'qiydi. - Kirish animatsiyasining narxi o'lchandi: LCP render kechikishi 345 → 132 ms; kanon holat 38 da uni olib tashladi.
Keyingi dars: Maxsus fayllar: loading, error, not-found — /loyihalar/yoq ning 200 statusini tuzatamiz va xato, yuklanish sahifalarini qo'shamiz.
Manbalar
- Next.js hujjati —
layout.js: https://nextjs.org/docs/app/api-reference/file-conventions/layout (2026-10-21 da o'qildi) - Next.js hujjati —
template.js: https://nextjs.org/docs/app/api-reference/file-conventions/template - Next.js xato sahifasi — Missing root layout tags: https://nextjs.org/docs/messages/missing-root-layout-tags
- web.dev — LCP qismlari (element render delay): https://web.dev/articles/optimize-lcp
- Kanon o'lchovlari:
portfoliov2, holat 04 va holat 37/38 Lighthouse (13.5.0, mobil, Chrome 155); layout/template tajribasi va root layout xatosi — biz ishga tushirdik (next 16.4.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!