В традиционных архитектурах 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').