1. Главные ограничения Server Components
Вот тут и начинается самое интересное. Server Components — это не "React как обычно", только быстрее. У них есть принципиальные ограничения, которые нужно понимать, чтобы не наступать на грабли.
Нет интерактивности
Server Component не может быть интерактивным.
Что это значит? Всё, что связано с обработкой событий на клиенте (клики, ввод текста, анимации, изменение состояния, работа с localStorage и т.п.), невозможно реализовать внутри Server Component.
Почему так?
- Сервер не может реагировать на события, происходящие в браузере пользователя.
- Код Server Component вообще не попадает в браузер — его там просто нет! Он "живёт" только на сервере, а клиент получает только результат рендера (HTML) и данные.
Примеры того, что не работает в Server Component
- onClick, onChange и любые другие обработчики событий.
- useState, useEffect, useReducer, useRef и любые React-хуки для состояния или эффектов.
- Работа с браузерным API: window, document, localStorage, alert, prompt и т.д.
- Динамическая навигация через useRouter().push() и т.п.
Если вы попробуете использовать что-то из этого в Server Component, Next.js вас строго отругает ошибкой на этапе сборки.
Нет состояния (state)
Server Component не может хранить или изменять состояние на клиенте.
— Нельзя использовать useState, useReducer, или любые другие хуки состояния.
— Нельзя делать что-то вроде:
// ❌ Не сработает!
const [count, setCount] = useState(0);
— Нельзя менять состояние по клику, потому что клика сервер "не увидит".
Нет эффектов (side effects)
Server Component не может использовать хуки жизненного цикла или эффекты (useEffect, useLayoutEffect и т.д.), потому что они нужны только на клиенте. Серверу не нужно "подписываться" на что-то, "чистить" после себя или реагировать на изменение DOM.
Нет доступа к браузерным API
Server Component не может обращаться к window, document, localStorage, sessionStorage, navigator и прочим прелестям браузерного мира. Всё это существует только в браузере, а не на сервере.
2. Что делать с ограничениями
Можно подумать: "Эй, зачем такие жёсткие рамки? Почему нельзя просто сделать всё как раньше?"
Причина проста: Server Component — это не код для браузера, а код для сервера! Его задача — сгенерировать HTML и данные, которые потом попадут в браузер. Он не может и не должен реагировать на действия пользователя после того, как страница уже загружена.
Это похоже на ресторан: повар на кухне (сервер) готовит блюдо, а вы (клиент) уже едите его за столом. Повар не может добавить вам соли по щелчку пальцев — он уже ушёл готовить для других клиентов.
Плюсы такого подхода:
- Меньше JS на клиенте — быстрее загрузка, меньше ресурсов.
- Можно делать тяжёлые операции (запросы, обработку данных) на сервере, не нагружая браузер.
- Безопасность: секретные ключи и доступ к базе данных не утекают на клиент.
- Легче поддерживать SEO и рендерить страницы для поисковиков.
Как понять, что компоненту нужна интерактивность?
Очень просто: если компонент должен реагировать на действия пользователя после загрузки страницы — это Client Component.
Примеры:
- Кнопка "лайк", которая увеличивает счётчик при клике.
- Форма с полями ввода, где пользователь что-то пишет.
- Выпадающее меню, которое открывается по наведению.
- Любая анимация, зависящая от действий пользователя.
Противоположные примеры (Server Component):
- Список статей, полученных из базы данных.
- Статичная информация о товаре.
- SEO-метаданные, Open Graph, хлебные крошки.
- Любой контент, который не меняется без действий пользователя.
Как сделать компонент интерактивным? ("use client")
Когда вам нужна интерактивность, оборачивайте нужный компонент в Client Component. Для этого достаточно добавить в начало файла строку:
"use client";
Теперь внутри этого компонента можно использовать useState, useEffect, обработчики событий, работать с window и прочее. Такой компонент будет загружен на клиент и сможет реагировать на действия пользователя.
Важно:
— Весь код в Client Component попадёт в JS-бандл и будет исполняться в браузере.
— Можно смешивать Server и Client Components: Server "собирает" структуру страницы, Client "оживляет" отдельные части.
3. Примеры: что можно и что нельзя
Пример 1: Server Component (только отображение данных)
// app/products/page.jsx (Server Component по умолчанию)
import { getProducts } from "@/lib/db";
export default async function ProductsPage() {
const products = await getProducts();
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
Здесь всё хорошо:
— Нет интерактивности, только вывод данных.
— Можно делать async/await, обращаться к базе.
Пример 2: Ошибка — попытка обработать событие в Server Component
// ❌ Это не сработает!
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)>
Clicked {count} times
</button>
);
}
Результат:
Next.js ругнётся: "useState нельзя использовать в Server Component".
Пример 3: Как правильно — разделяем Server и Client Components
Server Component:
import Counter from "./Counter";
export default function Page() {
return (
<main>
<h1>Добро пожаловать!</h1>
<Counter />
</main>
);
}
Client Component (Counter.jsx):
"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)>
Кликнули {count} раз
</button>
);
}
Теперь всё работает:
— Server Component рендерит всю страницу.
— Client Component "оживляет" только нужную часть.
4. Полезные нюансы
Что можно и нельзя в Server Components
| Возможность | Server Component | Client Component |
|---|---|---|
| useState, useEffect | ❌ | ✅ |
| Обработчики событий (onClick, onChange и т.д.) | ❌ | ✅ |
| Доступ к window, document | ❌ | ✅ |
| async/await (fetch, db) | ✅ | ✅ |
| Работа с localStorage | ❌ | ✅ |
| Рендер на сервере | ✅ | ✅ |
| Входит в JS-бандл клиента | ❌ | ✅ |
Как это влияет на архитектуру приложения
Server Components отлично подходят для:
- Получения и отображения данных с сервера.
- Генерации страниц для SEO.
- Быстрого рендера больших объёмов информации.
- Секретных операций (доступ к базе, API-ключи).
Client Components нужны для:
- Любой интерактивности (формы, кнопки, анимации).
- Хранения состояния на клиенте.
- Работы с браузерными API.
Совет:
Делайте как можно больше компонентов серверными, а клиентскими — только то, что действительно нужно "оживить". Это уменьшит размер JS, ускорит загрузку, и сделает приложение более отзывчивым.
5. Типичные ошибки при работе с Server Components
Ошибка №1: попытка использовать useState или обработчики событий в Server Component.
Это не сработает: Next.js выдаст ошибку. Не забывайте: никакой интерактивности на сервере!
Ошибка №2: попытка обратиться к window, document, localStorage в Server Component.
Эти объекты существуют только в браузере. На сервере их нет, и попытка обращения вызовет ошибку.
Ошибка №3: забыли добавить "use client" в компонент с интерактивностью.
Если компонент должен реагировать на клики, а вы не сделали его клиентским — ничего работать не будет, а Next.js предупредит об ошибке.
Ошибка №4: чрезмерное использование Client Components.
Если вы делаете всё клиентским "на всякий случай", теряются преимущества Server Components: увеличивается размер бандла, страница грузится дольше.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ