
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 │
└─────────────────────────────────────────────────┘
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
// 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>
);
}
// 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>
);
}
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
'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 ──────────────┤
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>
);
}
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>
</>
);
}
// app/ui/search.tsx
'use client';
export default function Search() {
// useState, onChange va boshqa interaktiv logika
}
// 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
}
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!
}
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>
)}
</>
);
}
// 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>
);
}
// 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>
);
}
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
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>
);
}
// 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>
);
}
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
// 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>
);
}
// 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>
);
}
// 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>;
}
// 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>
);
}
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>;
}
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 />;
}
Yechim - re-export wrapper:
// app/ui/carousel.tsx
'use client';
import { Carousel } from 'acme-carousel';
export default Carousel; // Shunchaki re-export
// app/page.tsx - Endi ishlaydi
import Carousel from './ui/carousel';
export default function Page() {
return <Carousel />;
}
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();
}
// ✅ 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();
}
O'rnatish:
pnpm add server-only # server-only kodni himoyalash
pnpm add client-only # client-only kodni belgilash
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
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)
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!
Top comments (0)