У традиційних архітектурах React існував поділ: або весь компонент виконується на клієнті, або сервер робить SSR, але весь код все одно відправляється в браузер для повної гідратації.
Next.js 16 та React 19 запроваджують революційну дуальну модель компонентів:
DATABASE_URL, API-ключів). Код серверних компонентів та їхні залежності ніколи не потрапляють у бандл браузера (Zero Bundle Size).'use client'. Вони рендеряться на сервері у вигляді початкового HTML, а потім завантажуються браузером і проходять гідратацію (Hydration), отримуючи можливість використовувати хуки (useState, useEffect), браузерні API (localStorage, геолокація) та слухачі подій (onClick, onChange).Директива 'use client' — це не інструкція «виконувати лише в браузері». Це оголошення точки розділення модулів (Network Boundary): все, що імпортується під цією директивою, упаковується збирачем у клієнтський JavaScript-бандл.
Погляньмо на дерево компонентів та межу гідратації:
+-------------------------------------------------------------------------+
| МЕНТАЛЬНА МОДЕЛЬ МЕЖІ 'use client' |
+-------------------------------------------------------------------------+
| СЕРВЕРНА ЗОНА (Node.js runtime, доступ до БД, нульовий JS у бандлі): |
| [Page (RSC)] ---> читає БД напряму: SELECT * FROM products |
| | |
| +--- [ProductHeader (RSC)] ---> статичний серверний рендеринг |
| | |
| ===== МЕЖА 'use client' =============================================== |
| | |
| КЛІЄНТСЬКА ЗОНА (Завантажується браузером, гідратується, інтерактивна): |
| +--- [AddToCartButton ('use client')] |
| - використовує useState, onClick |
| - анімується та зберігає локальний стан |
+-------------------------------------------------------------------------+На сервері виконуються всі RSC. Вони звертаються до баз даних та готують сирі дані.
Сервер генерує початковий HTML для браузера та Flight-дескриптори для передачі даних клієнту.
Клієнтські компоненти також запускаються на сервері для формування початкового візуального каркаса (SSR).
Браузер оживляє лише клієнтські острови з директивою 'use client', ігноруючи серверний код.
Що саме означає додавання рядка `'use client'` на самий початок файлу компонента?
process.env.STRIPE_SECRET_KEY всередині Server Components без ризику витоку в браузер.'use client' лише на кнопку додавання в кошик або рядок пошуку, зберігаючи 95% сторінки серверною.children: передача важкого серверного контенту всередину інтерактивного модального вікна без перетворення контенту на клієнтський код.Серверні компоненти можуть бути асинхронними (async/await) і не потребують useEffect для запитів:
// Це Server Component за замовчуванням!
// Жодного 'use client' тут немає!
interface Product {
id: string;
name: string;
price: number;
}
// Прямий асинхронний запит до серверного джерела
async function getProducts(): Promise<Product[]> {
// Пряме звернення до БД або зовнішнього API
const res = await fetch('https://api.wpskills.dev/v1/products', {
// У Next.js 16 кешування налаштовується через 'use cache' або параметри fetch
next: { revalidate: 60 },
});
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<div className="max-w-5xl mx-auto py-8">
<h1 className="text-2xl font-bold mb-6">Каталог обладнання</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{products.map(product => (
<div key={product.id} className="p-4 rounded-xl border border-border bg-bg-surface">
<h3 className="font-semibold text-text-primary">{product.name}</h3>
<p className="text-accent-cyan font-bold mt-2">${product.price}</p>
{/* Клієнтська інтерактивна кнопка */}
<AddToCartButton productId={product.id} />
</div>
))}
</div>
</div>
);
}Виносимо 'use client' суворо на інтерактивний листовий вузол:
'use client'; // ОБОВ'ЯЗКОВО: оголошуємо клієнтську межу!
import React, { useState } from 'react';
export function AddToCartButton({ productId }: { productId: string }) {
const [isAdded, setIsAdded] = useState(false);
const handleAdd = () => {
setIsAdded(true);
// Логіка додавання в кошик (localStorage або zustand)
console.log(`Товар ${productId} додано до кошика`);
};
return (
<button
onClick={handleAdd}
className={`mt-4 w-full py-2 px-4 rounded-lg text-sm font-semibold transition-colors ${
isAdded
? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30'
: 'bg-accent-cyan text-bg-base hover:opacity-90'
}`}
>
{isAdded ? 'Додано до кошика ✓' : 'Купити'}
</button>
);
}Клієнтський компонент не може напряму імпортувати серверний компонент (import ServerComp from './ServerComp' перетворить його на клієнтський).
Рішення: Передавати серверний компонент через проп children!
// 1. Клієнтський контейнер (ModalWrapper.tsx):
'use client';
import { useState } from 'react';
export function ModalWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(true)}>Відкрити</button>
{isOpen && <div className="modal-backdrop">{children}</div>}
</div>
);
}
// 2. Серверна сторінка (page.tsx):
// ServerComponent рендериться на сервері та передається як готовий children!
export default function Page() {
return (
<ModalWrapper>
{/* HeavyServerTable виконається НА СЕРВЕРІ та не збільшить бандл! */}
<HeavyServerTable />
</ModalWrapper>
);
}Чому передача серверного компонента через проп children у клієнтський компонент (`<ClientModal><ServerList /></ClientModal>`) зберігає його виконання на сервері?
Якщо помістити 'use client' на весь файл page.tsx, вся сторінка та всі її дочірні імпортовані модулі стануть клієнтськими, позбавляючи вас переваг Server Components.
Рішення: Залишайте сторінки та лейаути серверними (Server Components), а 'use client' спускайте вниз по дереву до конкретних інтерактивних кнопок, форм та віджетів (Move Client Boundary Down).
Серверний компонент передає пропси в клієнтський через мережевий протокол серіалізації Flight. Не можна передавати функції, інстанси кастомних класів або циклічні посилання:
// ПОМИЛКА: функції не можна серіалізувати через межу Server -> Client!
<ClientComponent onClick={() => console.log('server')} />Функції-обробники повинні оголошуватися всередині самого клієнтського компонента або передаватися через Server Actions ('use server').