Серверные компоненты перестали быть экспериментом: они по умолчанию включены в Next.js App Router и постепенно приходят в другие фреймворки. Главное обещание — меньше JavaScript в браузере. Ниже разбираем, когда обещание выполняется, а когда нет.
В чём идея
Обычный React-компонент попадает в браузер целиком: и разметка, и логика, и все зависимости. Серверный компонент выполняется на сервере, а в браузер уезжает только результат — готовый кусок разметки. Код компонента и его библиотеки в бандл не попадают вообще.
Практический эффект виден на страницах, где много «мёртвой» логики: форматирование дат, разбор Markdown, подсветка синтаксиса, работа с датами и валютами. Всё это тянет за собой тяжёлые библиотеки, которые пользователю не нужны.
// Серверный компонент: библиотека остаётся на сервере
import { format } from 'date-fns/locale/ru'
export default async function ProjectCard({ id }) {
const project = await db.project.find(id)
return (
<article>
<h3>{project.title}</h3>
<time>{format(project.date, 'd MMMM yyyy')}</time>
</article>
)
}
Что получилось на наших проектах
Мы перевели на серверные компоненты каталог интернет-магазина стройматериалов — около 4 200 товаров с фильтрами. Замеры до и после на одной и той же странице категории:
- JavaScript в браузере: 412 КБ → 168 КБ (после gzip)
- Largest Contentful Paint на 4G: 2.9 с → 1.6 с
- Interaction to Next Paint: 240 мс → 130 мс
Основной выигрыш дали не сами компоненты, а то, что вместе с ними ушли библиотеки форматирования и клиентский слой запросов к API. Фильтры остались клиентскими — им нужна интерактивность.
Важно
Серверные компоненты не ускоряют сайт сами по себе. Они убирают JavaScript, который вы и так могли не грузить. Если на странице почти нет логики — выигрыша не будет.
Где серверные компоненты мешают
Есть три ситуации, в которых мы сознательно остаёмся на клиентских компонентах.
Формы со сложной валидацией
Пока пользователь печатает, состояние должно жить в браузере. Попытка держать его на сервере превращается в поток запросов и заметные задержки. Форму брифа мы оставили клиентской.
Виджеты, зависящие от размера окна
Карусели, карты, всё, что читает window и размеры элементов, требует браузера. Серверный рендер здесь только добавляет прослойку.
Страницы с высокой частотой обновления
Если данные меняются раз в несколько секунд, кэширование серверного рендера работает против вас: приходится либо отключать кэш, либо мириться с устаревшими данными.
Как решаем, что делать серверным
- Открываем страницу и смотрим, какие компоненты вообще реагируют на действия пользователя.
- Всё остальное помечаем как кандидатов на серверный рендер.
- Проверяем размер бандла до и после — если меньше 20 КБ разницы, не трогаем.
- Замеряем INP на реальном мобильном устройстве, а не в эмуляторе.
Правило простое: серверным делаем то, что не отвечает на клики. Всё, что отвечает, оставляем в браузере.
Стоит ли переписывать существующий проект
Если сайт работает и укладывается в Core Web Vitals — нет. Переход требует смены роутинга и слоя данных, это недели работы. Смысл появляется, когда вы и так планируете редизайн или упёрлись в вес бандла и не можете его уменьшить обычными способами.
Где проходит граница между серверным и клиентским
Практическое правило простое: серверным делается всё, что только показывает данные, клиентским — то, что реагирует на пользователя. Карточка товара с ценой и описанием — серверная. Кнопка «в корзину» внутри неё — клиентская, и она может быть маленьким отдельным компонентом.
Ошибка, которую мы совершили на первом проекте, — сделали клиентским весь блок ради одной кнопки. В браузер уехало всё: разметка карточки, форматирование цены, библиотека дат. Разделили — и объём кода на странице каталога упал примерно на треть без единого изменения в интерфейсе.
Второе следствие менее очевидно: серверный компонент может обращаться к базе напрямую, без промежуточного HTTP-запроса. Пропадает целый слой — эндпоинты, которые существовали только чтобы отдать данные собственному фронтенду. Меньше кода, меньше мест, где данные расходятся.
Когда серверные компоненты не нужны
Если интерфейс почти целиком интерактивный — редактор, конструктор, панель с графиками, — выигрыш будет небольшим, а сложность вырастет. Разделение на серверное и клиентское требует дисциплины, и на проекте, где всё клиентское, эта дисциплина не окупается.
Не стоит переводить и работающий проект целиком ради самой архитектуры. Мы переводили постранично, начиная с каталога и статей — там больше всего разметки и меньше всего интерактива. Личный кабинет остался как был, и это оказалось правильным решением.
Частые вопросы
Серверные компоненты заменяют серверный рендеринг?
Нет, это разные вещи. Серверный рендеринг отдаёт готовый HTML, а потом весь код всё равно уезжает в браузер для «оживления». Серверные компоненты означают, что часть кода в браузер не попадает вообще — оживлять там нечего.
Работает ли это с обычным React без Next.js?
Технически возможность есть в самом React, но нужна поддержка сборщика и роутера. На практике сегодня это означает фреймворк — Next.js или аналог. Отдельно, в обычном приложении, настраивать это трудоёмко и хрупко.
На новых проектах мы по умолчанию начинаем с серверных компонентов и добавляем клиентские точечно — так проще, чем идти в обратную сторону.