DEV Community

MATKARIM MATKARIMOV
MATKARIM MATKARIMOV

Posted on

Next.js - Layoutlar va Sahifalar (Layouts & Pages) To'liq Qo'llanma


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)
Enter fullscreen mode Exit fullscreen mode
// app/page.tsx
export default function Page() {
  return <h1>Hello Next.js!</h1>;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

URL segmentlar tushuntirilishi:

/blog/my-post
 │     │    │
 │     │    └── [slug] - Leaf Segment (oxirgi)
 │     └── blog - Segment
 └── / - Root Segment
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode
// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

/blog/my-post sahifasi qanday ko'rinadi:

┌─────────────────────────────────────┐
│ Asosiy Navigatsiya (Root Layout)    │
│ ┌─────────────────────────────────┐ │
│ │ Blog Bo'limi (Blog Layout)      │ │
│ │ ┌──────────┐ ┌────────────────┐ │ │
│ │ │ Blog     │ │ Blog Post      │ │ │
│ │ │ Sidebar  │ │ Content        │ │ │
│ │ │          │ │ (page.tsx)     │ │ │
│ │ └──────────┘ └────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Muhim farq: Foydalanuvchi /blog/post-1 dan /blog/post-2 ga o'tganda:

  • Root Layout qayta renderlanmaydi
  • Blog Layout qayta renderlanmaydi
  • Faqat page.tsx yangilanadi

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Eslatma: Next.js 15+ da params asinxron bo'ldi, await qilish 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

<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>;
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode
// app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) {
  return <section>{props.children}</section>;
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/page.tsx
export default function HomePage() {
  return <h1>Blogimga xush kelibsiz!</h1>;
}
Enter fullscreen mode Exit fullscreen mode
// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode
// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode
// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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)