
Next.js 15+ da sahifalar yaratish, layoutlarni ichma-ich joylashtirish, dinamik routelar, searchParams va Link komponenti
Next.js App Router - Layoutlar va Sahifalar (Layouts & Pages)
Next.js da routing fayl tizimiga asoslangan - papka va fayllar orqali routelar yaratiladi. Bu maqolada sahifalar, layoutlar, dinamik routelar va navigatsiya haqida batafsil tushuntiraman.
1. Sahifa yaratish (Page)
Sahifa - bu ma'lum bir URL da ko'rsatiladigan UI. page.tsx fayl yaratib, default export qilasiz:
app/
└── page.tsx # / (bosh sahifa)
// app/page.tsx
export default function Page() {
return <h1>Hello Next.js!</h1>;
}
Qoida: Har doim default export bo'lishi shart. Named export (export const Page) ishlamaydi!
2. Layout yaratish
Layout - bir nechta sahifalar orasida umumiy bo'lgan UI (header, sidebar, footer). Layoutning eng muhim xususiyatlari:
- Navigatsiya qilganda qayta renderlanmaydi
- State saqlanib qoladi
- Interaktiv bo'lib qoladi
// app/layout.tsx - Root Layout (MAJBURIY)
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uz">
<body>
<header>
<nav>Navigatsiya</nav>
</header>
<main>{children}</main>
<footer>Footer 2026</footer>
</body>
</html>
);
}
children - bu sahifa yoki ichki layout. Foydalanuvchi /about ga o'tsa, children o'rniga app/about/page.tsx renderlanadi.
Muhim qoidalar:
- Root layout (
app/layout.tsx) majburiy - Unda
<html>va<body>teglari bo'lishi shart - Har bir papkada o'zining alohida layout bo'lishi mumkin
3. Ichki (Nested) Routelar yaratish
Papkalarni ichma-ich joylashtirish orqali URL segmentlar hosil bo'ladi:
app/
├── layout.tsx # Root layout
├── page.tsx # /
└── blog/
├── layout.tsx # Blog layout
├── page.tsx # /blog
└── [slug]/
└── page.tsx # /blog/my-post
URL segmentlar tushuntirilishi:
/blog/my-post
│ │ │
│ │ └── [slug] - Leaf Segment (oxirgi)
│ └── blog - Segment
└── / - Root Segment
Amaliy misol - Blog sahifasi:
// app/blog/page.tsx - /blog sahifasi
import Link from 'next/link';
import { getPosts } from '@/lib/posts';
export default async function BlogPage() {
const posts = await getPosts(); // Server Component - to'g'ridan-to'g'ri API chaqirish
return (
<ul>
{posts.map(post => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
);
}
4. Ichki Layoutlar (Nested Layouts)
Layoutlar avtomatik ravishda ichma-ich joylashadi. Har bir papkadagi layout o'z children ini o'rab oladi:
app/
├── layout.tsx # 1-daraja: Root Layout
├── page.tsx
└── blog/
├── layout.tsx # 2-daraja: Blog Layout
├── page.tsx
└── [slug]/
└── page.tsx
// app/layout.tsx - Root Layout
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uz">
<body>
<nav>Asosiy Navigatsiya</nav>
{children} {/* <-- bu yerga BlogLayout kiradi */}
</body>
</html>
);
}
// app/blog/layout.tsx - Blog Layout
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<section>
<h2>Blog Bo'limi</h2>
<aside>Blog Sidebar</aside>
<div>{children}</div> {/* <-- bu yerga blog sahifalar kiradi */}
</section>
);
}
/blog/my-post sahifasi qanday ko'rinadi:
┌─────────────────────────────────────┐
│ Asosiy Navigatsiya (Root Layout) │
│ ┌─────────────────────────────────┐ │
│ │ Blog Bo'limi (Blog Layout) │ │
│ │ ┌──────────┐ ┌────────────────┐ │ │
│ │ │ Blog │ │ Blog Post │ │ │
│ │ │ Sidebar │ │ Content │ │ │
│ │ │ │ │ (page.tsx) │ │ │
│ │ └──────────┘ └────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
Muhim farq: Foydalanuvchi /blog/post-1 dan /blog/post-2 ga o'tganda:
- Root Layout qayta renderlanmaydi
- Blog Layout qayta renderlanmaydi
- Faqat
page.tsxyangilanadi
Bu juda tez navigatsiya beradi!
5. Dinamik Segmentlar (Dynamic Routes)
Kvadrat qavslar [param] bilan parametrli sahifalar yaratiladi. params prop orqali qiymatni olasiz:
// app/blog/[slug]/page.tsx
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params; // params - Promise, await qilish kerak!
const post = await getPost(slug);
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}
Eslatma: Next.js 15+ da
paramsasinxron bo'ldi,awaitqilish shart.
| URL |
slug qiymati |
|---|---|
/blog/nextjs-kirish |
"nextjs-kirish" |
/blog/react-hooks |
"react-hooks" |
/blog/typescript-asoslari |
"typescript-asoslari" |
6. Search Params bilan ishlash
URL dagi ?key=value parametrlarini o'qish uchun searchParams prop ishlatiladi:
// app/products/page.tsx
// URL: /products?category=electronics&page=2
export default async function ProductsPage({
searchParams,
}: {
searchParams: Promise<{
[key: string]: string | string[] | undefined;
}>;
}) {
const { category, page } = await searchParams; // Bu ham Promise!
// category = "electronics"
// page = "2"
const products = await getProducts({
category,
page: Number(page),
});
return (
<div>
<h1>Mahsulotlar: {category}</h1>
{products.map(p => (
<div key={p.id}>{p.name}</div>
))}
</div>
);
}
Qachon nimani ishlatish kerak?
| Usul | Qachon ishlatiladi | Misol |
|---|---|---|
searchParams prop |
Server Component da ma'lumot yuklash uchun | Paginatsiya, filter, qidiruv |
useSearchParams() hook |
Client Component da UI o'zgartirish uchun | Allaqachon yuklangan listni filtrlash |
new URLSearchParams(window.location.search) |
Event handler ichida | Button click da URL ni o'qish (re-render bo'lmaydi) |
Client Component misoli:
'use client';
import { useSearchParams } from 'next/navigation';
export function SearchFilter() {
const searchParams = useSearchParams();
const category = searchParams.get('category'); // "electronics"
return <p>Tanlangan kategoriya: {category}</p>;
}
7. Sahifalar orasida navigatsiya - <Link>
<Link> komponenti - Next.js da sahifalar orasida o'tish uchun asosiy usul:
import Link from 'next/link';
export default function Navigation() {
return (
<nav>
<Link href="/">Bosh sahifa</Link>
<Link href="/blog">Blog</Link>
<Link href="/about">Biz haqimizda</Link>
{/* Dinamik link */}
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</nav>
);
}
<Link> ning afzalliklari:
- Prefetching - sahifa oldindan yuklab olinadi (viewport ga kirganida)
- Client-side navigation - to'liq sahifa yangilanmaydi, faqat o'zgargan qism
- Layout saqlanadi - layout qayta renderlanmaydi
Murakkab navigatsiya uchun useRouter hook ishlatiladi:
'use client';
import { useRouter } from 'next/navigation';
export function LogoutButton() {
const router = useRouter();
const handleLogout = async () => {
await logout();
router.push('/login'); // Sahifaga o'tish
router.replace('/login'); // Tarixda oldingi sahifa saqlanmaydi
router.refresh(); // Joriy sahifani yangilash
};
return <button onClick={handleLogout}>Chiqish</button>;
}
8. Route Props Helpers (TypeScript)
Next.js 15+ da PageProps va LayoutProps yordamchi tiplar mavjud. Bular import qilmasdan ishlatiladi:
// app/blog/[slug]/page.tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const { slug } = await props.params;
return <h1>Blog post: {slug}</h1>;
}
// app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) {
return <section>{props.children}</section>;
}
Bu tiplar next dev, next build yoki next typegen buyruqlari ishga tushirilganda avtomatik generatsiya qilinadi.
9. To'liq amaliy misol
Keling, hamma narsani birlashtiramiz - oddiy blog loyihasi:
app/
├── layout.tsx # Root Layout
├── page.tsx # / (Bosh sahifa)
└── blog/
├── layout.tsx # Blog Layout (sidebar bilan)
├── page.tsx # /blog (postlar ro'yxati)
└── [slug]/
└── page.tsx # /blog/post-nomi (bitta post)
// app/layout.tsx
import Link from 'next/link';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uz">
<body>
<header>
<nav>
<Link href="/">Bosh sahifa</Link>
<Link href="/blog">Blog</Link>
</nav>
</header>
<main>{children}</main>
<footer>2026 My Blog</footer>
</body>
</html>
);
}
// app/page.tsx
export default function HomePage() {
return <h1>Blogimga xush kelibsiz!</h1>;
}
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div style={{ display: 'flex' }}>
<aside>
<h3>Kategoriyalar</h3>
<ul>
<li>React</li>
<li>Next.js</li>
<li>TypeScript</li>
</ul>
</aside>
<section>{children}</section>
</div>
);
}
// app/blog/page.tsx
import Link from 'next/link';
import { getPosts } from '@/lib/posts';
export default async function BlogPage() {
const posts = await getPosts();
return (
<div>
<h1>Barcha postlar</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
</div>
);
}
// app/blog/[slug]/page.tsx
import { getPost } from '@/lib/posts';
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return (
<article>
<h1>{post.title}</h1>
<time>{post.date}</time>
<p>{post.content}</p>
</article>
);
}
Xulosa
| Tushuncha | Fayl | Xususiyati |
|---|---|---|
| Page | page.tsx |
default export, har bir route uchun alohida |
| Layout | layout.tsx |
children prop, navigatsiyada qayta renderlanmaydi
|
| Root Layout | app/layout.tsx |
Majburiy, <html> va <body> bo'lishi shart |
| Nested Layout | app/blog/layout.tsx |
Avtomatik ichma-ich joylashadi |
| Dynamic Route |
[slug] papka |
params orqali qiymat olinadi (Promise!) |
| Search Params |
searchParams prop |
URL query parametrlarini o'qish |
| Link | <Link href=""> |
Prefetching + client-side navigation |
| useRouter |
useRouter() hook |
Programmatik navigatsiya (client only) |
Eng muhim esda tutish kerak bo'lgan narsa: Layout bir marta renderlanadi va sahifalar o'zgarganda qayta renderlanmaydi - bu Next.js ning eng kuchli tomonlaridan biri!
Bu maqola Next.js 15+ rasmiy dokumentatsiyasi asosida yozilgan.
(https://www.matkarim.uz)
Top comments (0)