App Router — nima uchun yangi, pages router dan ustunligi
Muammodan boshlaylik
Siz Pages Router da dashboard yozyapsiz. Dashboard da sidebar bor — barcha sahifalarda ko'rinishi kerak. Siz _app.tsx ga sidebar qo'yasiz. Lekin endi sidebar barcha sahifalarda ko'rinadi — blog, landing page, hamma joyda. Siz faqat dashboard sahifalarida ko'rishni xohlagan edingiz.
Yechim? Har bir dashboard sahifasida if (route.startsWith('/dashboard')) tekshiruvi. Yoki alohida wrapper komponent. Har ikkalasi ham noqulay.
Bu Pages Router ning cheklovi. App Router aynan shu muammoni hal qilish uchun yaratildi.
Pages Router ning 3 ta asosiy muammosi
Muammo 1 — Har bir komponent brauzerda ishlaydi
Pages Router da barcha komponentlar brauzerda render bo'ladi. Bu degani — ma'lumotlar bazasiga to'g'ridan-to'g'ri ulanib bo'lmaydi, maxfiy API kalitlarni komponent ichida ishlatib bo'lmaydi.
Buning uchun alohida getServerSideProps yozish kerak:
// Pages Router — ma'lumot olish va ko'rsatish ikki alohida joy
export async function getServerSideProps() {
const users = await db.query('SELECT * FROM users')
return { props: { users } }
}
export default function UsersPage({ users }) {
return <ul>{users.map(u => <li>{u.name}</li>)}</ul>
}
Kichik loyihada yaxshi ko'rinadi. Lekin 30 ta sahifada 30 ta getServerSideProps — chalkash bo'ladi.
Muammo 2 — Nested layout qiyin
Yuqorida aytgan muammo. Dashboard sahifalari uchun alohida layout qilish — Pages Router da noqulay.
Muammo 3 — Loading va Error holatlari qo'lda
Ma'lumot yuklanayotganda loading spinner ko'rsatish, xato bo'lganda error sahifa ko'rsatish — har bir sahifada qo'lda yoziladi. Takrorlanuvchi kod.
App Router — yechim
Next.js 13 da App Router chiqdi. U yuqoridagi 3 ta muammoni hal qiladi.
1-yechim — Server Components
App Router da komponentlar default holda Server Component. Bu nima degani?
Server Component — serverda ishlaydigan komponent. Brauzerga faqat tayyor HTML yuboriladi, JavaScript yuborilmaydi.
Amalda bu shuni anglatadi: ma'lumotlar bazasiga to'g'ridan-to'g'ri ulanish mumkin, maxfiy kalitlar brauzerga chiqmaydi, async/await to'g'ridan-to'g'ri komponent ichida ishlaydi.
// app/users/page.tsx — Server Component (default)
export default async function UsersPage() {
// To'g'ridan-to'g'ri DB ga ulanish — bu kod serverda ishlaydi
const users = await db.query('SELECT * FROM users')
// Bu kalit brauzerga hech qachon chiqmaydi
const data = await fetch('https://api.example.com', {
headers: { 'Authorization': process.env.SECRET_KEY }
}).then(r => r.json())
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
Pages Router dagi 2 ta funksiya — App Router da 1 ta komponent. Ancha toza.
Server Component da nima ishlamaydi?
useState, useEffect, onClick, onChange — bular brauzer API. Server Component da brauzer yo'q, shuning uchun bular ishlamaydi.
Agar bular kerak bo'lsa — faylning boshiga 'use client' yozasiz. Bu komponentni Client Component ga aylantiradi.
'use client' // ← shu qator yetarli
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
💡 Qoida: Avval Server Component yozing. Faqat
useState,useEffect, event handler kerak bo'lganda'use client'qo'shing. Sababi — Server Component brauzerga JS yubormasligidan sahifa yengilroq bo'ladi.
2-yechim — Nested Layouts
Yuqoridagi dashboard muammosi App Router da papka tuzilmasi orqali hal bo'ladi:
app/
├── layout.tsx ← barcha sahifalar uchun (Navbar, Footer)
├── page.tsx ← /
└── dashboard/
├── layout.tsx ← faqat dashboard sahifalari uchun (Sidebar)
├── page.tsx ← /dashboard
└── settings/
└── page.tsx ← /dashboard/settings
/dashboard/settings ga kirganda sahifa qanday ko'rinadi:
┌──────────────────────────────────┐
│ Navbar (app/layout.tsx) │
├─────────┬────────────────────────┤
│ │ │
│ Sidebar │ Settings sahifasi │
│(dashb.. │ (settings/page.tsx) │
│layout) │ │
│ │ │
├─────────┴────────────────────────┤
│ Footer (app/layout.tsx) │
└──────────────────────────────────┘
Dashboard layouti faqat dashboard/ papkasi ichidagi sahifalarga qo'llanadi. Blog yoki landing page ga ta'sir qilmaydi.
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex">
<aside>
<a href="/dashboard">Bosh sahifa</a>
<a href="/dashboard/settings">Sozlamalar</a>
</aside>
<main>{children}</main>
</div>
)
}
Sahifa o'zgarganda faqat {children} yangilanadi — Navbar va Sidebar qayta render bo'lmaydi. Bu performance uchun ham yaxshi.
3-yechim — Maxsus fayllar
App Router da har bir route uchun maxsus fayllar bor. Ular avtomatik ishlaydi — qo'shimcha kod kerak emas.
app/dashboard/
├── page.tsx ← asosiy kontent
├── loading.tsx ← ma'lumot yuklanayotganda
├── error.tsx ← xato bo'lganda
└── not-found.tsx ← sahifa topilmaganda
loading.tsx — page.tsx ma'lumot yuklab bo'lguncha ko'rsatiladi:
// app/dashboard/loading.tsx
export default function Loading() {
return (
<div>
<p>Yuklanmoqda...</p>
</div>
)
}
error.tsx — page.tsx da xato bo'lganda ko'rsatiladi. Bu fayl Client Component bo'lishi shart — chunki reset funksiyasi brauzerda ishlaydi:
// app/dashboard/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<p>Xato: {error.message}</p>
<button onClick={reset}>Qayta urinish</button>
</div>
)
}
reset — sahifani qayta yuklash funksiyasi. Next.js o'zi beradi.
Ikkala router birga ishlatish mumkinmi
Ha. Eski loyihani App Router ga asta-sekin o'tkazayotgan bo'lsangiz — ikkalasi bir vaqtda ishlaydi:
my-app/
├── app/ ← yangi sahifalar (App Router)
│ └── about/
│ └── page.tsx
└── pages/ ← eski sahifalar (Pages Router)
└── index.tsx
Lekin yangi loyihada faqat App Router ishlatish tavsiya etiladi.
Tez-tez uchraydigan xatolar
1. Server Component da useState ishlatish
Bu eng ko'p uchraydigan xato. Yangi boshlovchilar 'use client' yozishni unutadi.
// ❌ — Server Component da useState bo'lmaydi
export default async function Page() {
const [open, setOpen] = useState(false) // xato!
}
Xato xabari: You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client".
// ✅ — 'use client' qo'shing
'use client'
export default function Page() {
const [open, setOpen] = useState(false)
}
2. error.tsx da 'use client' yo'q
// ❌ — error.tsx da onClick bor, lekin 'use client' yo'q
export default function Error({ reset }) {
return <button onClick={reset}>Qayta</button>
}
Xato xabari: Event handlers cannot be passed to Client Component props.
// ✅
'use client'
export default function Error({ reset }) {
return <button onClick={reset}>Qayta</button>
}
3. Ichki layoutda <html><body> yozish
// ❌ — faqat root layout.tsx da kerak
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
return (
<html>
<body>
<aside>Sidebar</aside>
{children}
</body>
</html>
)
}
Xato xabari: <html> cannot appear as a child of <body>.
// ✅
export default function DashboardLayout({ children }) {
return (
<div className="flex">
<aside>Sidebar</aside>
<main>{children}</main>
</div>
)
}
Xulosa
App Router — Pages Router ning 3 ta muammosini hal qiladi.
Server Components — komponentlar serverda ishlaydi, async/await to'g'ridan-to'g'ri, brauzerga JS yuborilmaydi. Brauzer API kerak bo'lsa — 'use client'.
Nested Layouts — papka tuzilmasi orqali ichma-ich qoliplar. Dashboard layouti faqat dashboard sahifalariga qo'llanadi.
Maxsus fayllar — loading.tsx, error.tsx avtomatik ishlaydi, qo'shimcha kod kerak emas. error.tsx har doim 'use client'.
Keyingi dars
4-dars: File-based Routing — papka va fayl orqali route yaratish
Dynamic route ([id]), nested route, route group — bularni amalda ko'ramiz.