1. Введение: зачем нужны Server Components?
Если вы когда-либо писали React-приложения, то знаете: весь ваш код работает в браузере пользователя, даже если он занимается, скажем, только загрузкой данных или подготовкой HTML. Это иногда напоминает ситуацию, когда вы зовёте официанта, чтобы он приготовил вам кофе… прямо у вас за столиком. Не очень удобно, правда?
Server Components (далее — SC или серверные компоненты) — это новая концепция, которая позволяет часть React-компонентов выполнять не в браузере, а на сервере. Это как если бы официант готовил кофе на кухне, а вам приносил уже готовый напиток.
Краткое определение
Server Component — это React-компонент, который выполняется на сервере, а не в браузере. Его результат (HTML, данные) отправляется пользователю, а JS-код компонента не попадает в клиентский бандл.
Как это работает? (Общая схема)
Давайте разберёмся на пальцах. Представьте, что у вас есть такая структура компонентов:
<App>
<Header />
<UserProfile />
<ProductList />
<Footer />
</App>
В классическом React все эти компоненты грузятся и работают в браузере. Но если Header, ProductList и Footer — просто отображают данные, которые можно подготовить на сервере, зачем тянуть их JS-код пользователю? Пусть сервер сам подготовит HTML, а браузер просто покажет его.
Вот как это выглядит с Server Components:
- Server Component: выполняется только на сервере, может обращаться к базе данных, секретам, API, и возвращает только результат (HTML, данные).
- Client Component: обычный React-компонент, работает в браузере, умеет реагировать на клики, хранить состояние, использовать хуки (useState, useEffect).
graph TD
A[Server Component] -->|HTML, данные| B(Браузер)
C[Client Component] -->|JS-код, интерактивность| B
2. Преимущества Server Components
Меньше JavaScript на клиенте
Главное преимущество: код Server Component не попадает в браузер. Это значит, что ваши пользователи не качают лишний JS, не ждут пока он загрузится и не тратят ресурсы браузера на его выполнение.
Пример:
Если компонент ProductList грузит товары из базы, фильтрует их, форматирует, а потом просто показывает таблицу — всё это может быть сделано на сервере. В браузер попадёт только готовый HTML.
Доступ к серверным ресурсам
Server Component может напрямую обращаться к базе данных, приватным API, файловой системе. Не нужно городить API-эндпоинты или прокидывать секреты на клиент.
// app/products/page.tsx
import { getProducts } from '@/lib/db';
export default async function ProductList() {
const products = await getProducts(); // Прямой доступ к базе!
return (
<ul>
{products.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
Улучшение производительности (и Core Web Vitals)
- Меньше JS — быстрее загрузка, выше рейтинг Google.
- Быстрее первый рендер: HTML генерируется на сервере.
- Меньше "hydration" — браузеру не нужно оживлять статичный контент.
Простота кода
- Не нужно писать лишние API-эндпоинты.
- Не нужно думать о синхронизации состояния между сервером и клиентом.
- Можно использовать обычные async/await, любые серверные библиотеки.
Безопасность
- Секреты (API-ключи, пароли) не уходят на клиент.
- Нет риска случайно "засветить" приватные данные.
3. Как узнать, что компонент — Server Component?
В Next.js 15 всё, что находится в папке app/ — по умолчанию Server Component.
// app/page.tsx
export default function Home() {
// Это серверный компонент!
return <h1>Привет с сервера!</h1>;
}
Как сделать компонент клиентским?
Если вам нужна интерактивность (клик, useState, useEffect), добавьте в начало файла директиву "use client":
// app/components/Counter.tsx
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Кликнули {count} раз
</button>
);
}
4. Пример: смешанная иерархия компонентов
Давайте соберём простую страницу интернет-магазина:
<App>
<Header /> // Server Component
<ProductList /> // Server Component
<ProductCard /> // Server Component
<AddToCartButton /> // Client Component
<Footer /> // Server Component
</App>
- ProductList и ProductCard могут быть серверными: они получают данные, отображают товары.
- AddToCartButton — клиентский компонент: только он реагирует на клики, хранит локальное состояние.
// app/products/ProductList.tsx
import ProductCard from './ProductCard';
import { getProducts } from '@/lib/db';
export default async function ProductList() {
const products = await getProducts();
return (
<div>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</div>
);
}
// app/products/ProductCard.tsx
import AddToCartButton from './AddToCartButton';
export default function ProductCard({ product }) {
return (
<div>
<h2>{product.title}</h2>
<AddToCartButton productId={product.id} />
</div>
);
}
// app/products/AddToCartButton.tsx
"use client";
export default function AddToCartButton({ productId }) {
return (
<button onClick={() => alert(`Добавили товар ${productId} в корзину!`)}>
В корзину
</button>
);
}
5. Полезные нюансы
Что можно и что нельзя в Server Components
| Можно | Нельзя |
|---|---|
|
|
Запомните:
Server Component — это про подготовку данных и HTML на сервере. Всё, что связано с интерактивностью — только в Client Component.
Как это влияет на структуру приложения?
Серверные компоненты — идеальны для страниц, списков, карточек, любых частей, которые не требуют интерактивности.
Клиентские компоненты — только для тех частей, где нужно реагировать на действия пользователя.
Это позволяет строить гибкие, быстрые и масштабируемые приложения, где большая часть кода работает на сервере, а клиенту достаётся только то, что действительно нужно.
Чем Server Components отличаются от SSR и SSG?
- SSR (Server-Side Rendering):
Сервер генерирует HTML, но весь JS-код всё равно отправляется на клиент, чтобы "оживить" страницу (hydration). - SSG (Static Site Generation):
HTML готовится заранее (на build), но опять же, JS-код компонентов попадает в браузер. - Server Components:
Генерируют HTML на сервере, но сам JS-код компонента не отправляется на клиент вообще. Только те компоненты, которые нужны для интерактивности, попадают в клиентский бандл.
6. Типичные ошибки и подводные камни
Ошибка №1: попытка использовать useState или useEffect в Server Component.
Компилятор Next.js вас тут же отругает: Hooks can only be used in Client Components. Не забывайте про "use client"!
Ошибка №2: импорт клиентского компонента в серверный без директивы.
Если вы импортируете компонент с "use client" в обычный файл, Next.js автоматически делает весь файл клиентским. Иногда это не то, что вы хотели — следите за этим.
Ошибка №3: попытка использовать window, document, localStorage и прочие браузерные API.
В Server Component их просто нет, потому что код выполняется на сервере. Если очень хочется — делайте отдельный Client Component.
Ошибка №4: ожидание, что Server Component "оживёт" на клиенте.
Server Component не может реагировать на клики, хранить состояние, подписываться на события. Для этого нужны Client Components.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ