DEV Community

MATKARIM MATKARIMOV
MATKARIM MATKARIMOV

Posted on

Next.js - Server va Client Componentlar To'liq Qo'llanma


Next.js 15+ da Server va Client Componentlarning farqi, 'use client' chegarasi, Context providerlar, 3rd-party kutubxonalar va xavfsizlik
tags: nextjs, react, webdev, beginners

Next.js App Router - Server va Client Componentlar

Bu mavzu Next.js ning eng muhim tushunchalaridan biri. Komponentlar qayerda renderlanishini tushunish - samarali Next.js ilova yaratishning kaliti.


Server va Client Component nima?

Next.js da barcha komponentlar default holatda Server Component. Client Component qilish uchun faylning yuqorisiga 'use client' yoziladi.

┌─────────────────────────────────────────────────┐
│              SERVER COMPONENT                    │
│  (default - hech narsa yozmasangiz shu)         │
│                                                 │
│  ✅ Ma'lumot olish (fetch, DB)                  │
│  ✅ Maxfiy kalitlar (API_KEY, tokens)           │
│  ✅ JS hajmi kichik (brauzerga JS yuborilmaydi) │
│  ✅ SEO yaxshi                                  │
│                                                 │
│  ❌ useState, useEffect ISHLAMAYDI              │
│  ❌ onClick, onChange ISHLAMAYDI                 │
│  ❌ Browser API (localStorage, window)          │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│              CLIENT COMPONENT                    │
│  ('use client' yozish kerak)                    │
│                                                 │
│  ✅ useState, useEffect                         │
│  ✅ onClick, onChange, onSubmit                  │
│  ✅ Browser API (localStorage, window)          │
│  ✅ Custom hooklar                              │
│                                                 │
│  ❌ async component bo'lolmaydi                 │
│  ❌ Maxfiy kalitlarni foydalanuvchi ko'radi     │
│  ❌ JS hajmi oshadi                             │
└─────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Qachon qaysi birini ishlatish kerak?

Vazifa Server Client
Ma'lumot olish (fetch, DB query)
API kalitlar, tokenlar bilan ishlash
Katta kutubxonalarni server tomonida ishlatish
JS hajmini kamaytirish
State boshqarish (useState)
Event handlerlar (onClick, onChange)
useEffect, lifecycle
Browser API (localStorage, window)
Custom hooklar (useForm, useQuery)

Amaliy misol - Server va Client birgalikda

app/
├── blog/
│   └── [id]/
│       └── page.tsx        # Server Component - ma'lumot oladi
└── ui/
    └── like-button.tsx     # Client Component - interaktiv
Enter fullscreen mode Exit fullscreen mode
// app/blog/[id]/page.tsx - SERVER COMPONENT (default)
// 'use client' YO'Q = Server Component

import LikeButton from '@/app/ui/like-button';
import { getPost } from '@/lib/data';

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const post = await getPost(id); // Server da DB/API ga murojaat

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      {/* Server → Client ga props orqali ma'lumot uzatish */}
      <LikeButton likes={post.likes} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/ui/like-button.tsx - CLIENT COMPONENT
'use client'; // SHU QATOR CLIENT COMPONENT QILADI

import { useState } from 'react';

export default function LikeButton({ likes }: { likes: number }) {
  const [likeCount, setLikeCount] = useState(likes);

  return (
    <button onClick={() => setLikeCount(likeCount + 1)}>
      ❤️ {likeCount}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Jarayon:

1. Server: Page renderlanadi → post ma'lumoti olinadi
2. Server: LikeButton uchun joy belgilanadi (placeholder)
3. Client: HTML darhol ko'rsatiladi (interaktiv emas hali)
4. Client: Hydration → LikeButton interaktiv bo'ladi
5. Foydalanuvchi: tugmani bosa oladi
Enter fullscreen mode Exit fullscreen mode

'use client' qanday ishlaydi - Chegara (Boundary)

'use client' - bu Server va Client orasidagi chegara. Bu faylga import qilingan barcha komponentlar ham Client Component ga aylanadi:

                    'use client' chegarasi
                           │
  SERVER TOMONI            │          CLIENT TOMONI
                           │
  layout.tsx ──────────────┤
  page.tsx ────────────────┤
                           │──── search.tsx ('use client')
                           │        ├── SearchInput.tsx  ← avtomatik Client
                           │        └── SearchResults.tsx ← avtomatik Client
                           │
  header.tsx ──────────────┤
  footer.tsx ──────────────┤
Enter fullscreen mode Exit fullscreen mode

SearchInput va SearchResults da 'use client' yozilmagan bo'lsa ham, ular search.tsx ichida import qilinganligi uchun avtomatik Client Component bo'ladi.


JS hajmini kamaytirish - ENG MUHIM TAMOYIL

Yomon yondashuv - butun Layout ni Client qilish:

// ❌ YOMON - hamma narsa clientga yuboriladi
'use client';

import Search from './search';
import Logo from './logo';

export default function Layout({ children }) {
  return (
    <nav>
      <Logo />      {/* Bu ham Client bo'lib qoladi - keraksiz! */}
      <Search />
    </nav>
  );
}
Enter fullscreen mode Exit fullscreen mode

Yaxshi yondashuv - faqat interaktiv qismni Client qilish:

// ✅ YAXSHI - Layout Server Component bo'lib qoladi
// app/layout.tsx (Server Component)
import Search from './ui/search';  // Client Component
import Logo from './ui/logo';      // Server Component bo'lib qoladi

export default function Layout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <>
      <nav>
        <Logo />       {/* Server - JS yuborilmaydi */}
        <Search />     {/* Faqat shu Client */}
      </nav>
      <main>{children}</main>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/ui/search.tsx
'use client';

export default function Search() {
  // useState, onChange va boshqa interaktiv logika
}
Enter fullscreen mode Exit fullscreen mode
// app/ui/logo.tsx - 'use client' YO'Q
export default function Logo() {
  return <img src="/logo.svg" alt="Logo" />;
  // Oddiy rasm - serverda renderlanadi, JS yuborilmaydi
}
Enter fullscreen mode Exit fullscreen mode

Natija: Brauzerga faqat Search komponenti uchun JS yuboriladi. Logo, Layout uchun JS yuborilmaydi.


Server Component ni Client Component ichiga joylashtirish

Muammo: Client Component ichida Server Component ni to'g'ridan-to'g'ri import qilib bo'lmaydi:

// ❌ ISHLAMAYDI
'use client';
import ServerComponent from './server-component';

export default function ClientComponent() {
  return <ServerComponent />; // Bu ham Clientga aylanadi!
}
Enter fullscreen mode Exit fullscreen mode

Yechim: children prop orqali uzating:

// app/ui/modal.tsx - CLIENT COMPONENT
'use client';

import { useState } from 'react';

export default function Modal({
  children,
}: {
  children: React.ReactNode;
}) {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Ochish</button>
      {isOpen && (
        <div className="modal">
          {children}  {/* Server Component shu yerga kiradi */}
        </div>
      )}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/ui/cart.tsx - SERVER COMPONENT
import { getCartItems } from '@/lib/data';

export default async function Cart() {
  const items = await getCartItems(); // Serverda ma'lumot oladi

  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/page.tsx - SERVER COMPONENT
import Modal from './ui/modal';
import Cart from './ui/cart';

export default function Page() {
  return (
    <Modal>
      <Cart />
      {/* Cart serverda renderlanadi, keyin Modal ichiga joylashadi */}
    </Modal>
  );
}
Enter fullscreen mode Exit fullscreen mode

Render tartibi:

1. Server: Cart renderlanadi (ma'lumot olinadi) → HTML tayyor
2. Server: Page renderlanadi → Modal placeholder + Cart HTML
3. Client: Modal hydrate bo'ladi → interaktiv
4. Client: Cart HTML o'z joyida — qayta render yo'q
Enter fullscreen mode Exit fullscreen mode

Context Providerlar

React Context Server Component da ishlamaydi. Provider yaratish uchun Client Component kerak:

// app/providers/theme-provider.tsx
'use client';

import { createContext, useState } from 'react';

export const ThemeContext = createContext({
  theme: 'light',
  toggle: () => {},
});

export default function ThemeProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider
      value={{
        theme,
        toggle: () =>
          setTheme(t => (t === 'light' ? 'dark' : 'light')),
      }}
    >
      {children}
    </ThemeContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/layout.tsx - SERVER COMPONENT
import ThemeProvider from './providers/theme-provider';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <ThemeProvider>
          {children}
          {/* children hali ham Server Component bo'lib qoladi */}
        </ThemeProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Muhim: Providerni iloji boricha chuqurroq joylashtiring. <html> ni emas, faqat {children} ni o'rab oling - shunda Next.js statik qismlarni optimizatsiya qila oladi.


Ma'lumotni Server va Client o'rtasida ulashish

React.cache bilan bitta so'rov ichida bir xil ma'lumotni takror yuklamaslik:

// app/lib/user.ts
import { cache } from 'react';

export const getUser = cache(async () => {
  const res = await fetch('https://api.example.com/user');
  return res.json();
});
// 1 ta so'rov ichida necha marta chaqirilsa ham,
// faqat 1 marta fetch bo'ladi
Enter fullscreen mode Exit fullscreen mode
// app/user-provider.tsx
'use client';

import { createContext } from 'react';

type User = { id: string; name: string };

export const UserContext =
  createContext<Promise<User> | null>(null);

export default function UserProvider({
  children,
  userPromise,
}: {
  children: React.ReactNode;
  userPromise: Promise<User>;
}) {
  return (
    <UserContext value={userPromise}>
      {children}
    </UserContext>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/layout.tsx - SERVER
import UserProvider from './user-provider';
import { getUser } from './lib/user';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const userPromise = getUser(); // await QILMANG!

  return (
    <html>
      <body>
        <UserProvider userPromise={userPromise}>
          {children}
        </UserProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode
// app/ui/profile.tsx - CLIENT
'use client';

import { use, useContext } from 'react';
import { UserContext } from '../user-provider';

export function Profile() {
  const userPromise = useContext(UserContext);
  const user = use(userPromise!); // use() Promise ni resolve qiladi

  return <p>Xush kelibsiz, {user.name}!</p>;
}
Enter fullscreen mode Exit fullscreen mode
// app/page.tsx - SERVER
import { Suspense } from 'react';
import { Profile } from './ui/profile';

export default function Page() {
  return (
    <Suspense fallback={<div>Yuklanmoqda...</div>}>
      <Profile />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

Server Component ham ishlatishi mumkin:

// app/dashboard/page.tsx - SERVER
import { getUser } from '../lib/user';

export default async function DashboardPage() {
  const user = await getUser(); // cache tufayli qayta fetch bo'lmaydi
  return <h1>Dashboard: {user.name}</h1>;
}
Enter fullscreen mode Exit fullscreen mode

Uchinchi tomon (3rd-party) kutubxonalar

Ba'zi kutubxonalar 'use client' yozmagan bo'lishi mumkin, lekin useState ishlatadi. Bunday holda wrapper yarating:

// ❌ XATO - Carousel useState ishlatadi, lekin 'use client' yo'q
// app/page.tsx (Server Component)
import { Carousel } from 'acme-carousel'; // Error!

export default function Page() {
  return <Carousel />;
}
Enter fullscreen mode Exit fullscreen mode

Yechim - re-export wrapper:

// app/ui/carousel.tsx
'use client';

import { Carousel } from 'acme-carousel';
export default Carousel; // Shunchaki re-export
Enter fullscreen mode Exit fullscreen mode
// app/page.tsx - Endi ishlaydi
import Carousel from './ui/carousel';

export default function Page() {
  return <Carousel />;
}
Enter fullscreen mode Exit fullscreen mode

Muhit xavfsizligi - server-only paketi

Server dagi maxfiy kodlar tasodifan clientga tushib qolmasligi uchun:

// ❌ XAVFLI - bu fayl clientda ham import qilinishi mumkin
// lib/data.ts
export async function getData() {
  const res = await fetch('https://api.com/data', {
    headers: {
      authorization: process.env.API_KEY,
      // Clientda bo'sh string bo'ladi!
    },
  });
  return res.json();
}
Enter fullscreen mode Exit fullscreen mode
// ✅ XAVFSIZ - client da import qilsangiz build xatolik beradi
// lib/data.ts
import 'server-only';

export async function getData() {
  const res = await fetch('https://api.com/data', {
    headers: {
      authorization: process.env.API_KEY,
    },
  });
  return res.json();
}
Enter fullscreen mode Exit fullscreen mode

O'rnatish:

pnpm add server-only    # server-only kodni himoyalash
pnpm add client-only    # client-only kodni belgilash
Enter fullscreen mode Exit fullscreen mode

Eslatma: Next.js da faqat NEXT_PUBLIC_ prefiksli environment o'zgaruvchilar clientga yuboriladi. Boshqalari serverda qoladi.


Rendering jarayoni - To'liq rasm

Birinchi yuklash (Initial Load):

SERVER                              CLIENT
──────                              ──────
1. Server Componentlar              4. HTML ko'rsatiladi
   renderlanadi                        (interaktiv emas hali)
         │                                    │
2. RSC Payload yaratiladi           5. RSC Payload bilan
   (binary format)                     Server/Client daraxtlar
         │                             birlashtiriladi
3. HTML pre-render qilinadi                   │
   (Server + Client)               6. Hydration - Client
         │                             Componentlar interaktiv
         └──── yuboriladi ──────►      bo'ladi
Enter fullscreen mode Exit fullscreen mode

Keyingi navigatsiyalar (Subsequent):

SERVER                              CLIENT
──────                              ──────
1. RSC Payload yaratiladi           2. Client Component
   (oldindan prefetch                  FAQAT clientda
    qilingan bo'lishi mumkin)          renderlanadi
         │                                    │
         └──── yuboriladi ──────►   3. DOM yangilanadi
                                       (HTML qayta yuklanmaydi)
Enter fullscreen mode Exit fullscreen mode

Hydration nima?

Hydration - React ning event handler larni DOM ga biriktirish jarayoni. Server dan kelgan statik HTML ni interaktiv qiladi. Tugma bosilishi, input yozilishi - bularning barchasi hydration dan keyin ishlaydi.


Xulosa

Tushuncha Qoida
Default holat Server Component
Client Component 'use client' yozasiz
'use client' chegarasi Shu fayl + barcha importlari Client bo'ladi
Server → Client ma'lumot Props orqali (serializable bo'lishi shart)
Server ni Client ichiga children prop orqali
Context Provider Client Component sifatida yaratiladi
Maxfiy kodlar himoyasi import 'server-only'
3rd-party kutubxonalar Wrapper: 'use client' + re-export
JS hajmini kamaytirish 'use client' ni faqat interaktiv componentga

Oltin qoida

'use client' ni iloji boricha past darajada (leaf component da) ishlatang. Shunda Server Component lar ko'p bo'ladi va brauzerga kamroq JS yuboriladi. Bu esa sahifangizni tezlashtiradi va foydalanuvchi tajribasini yaxshilaydi.


Bu maqola Next.js 15+ rasmiy dokumentatsiyasi asosida yozilgan. Savollar yoki takliflar bo'lsa, kommentariyada yozing!

(https://www.matkarim.uz)

Top comments (0)