Аннотация. React Server Components (RSC), представленные как экспериментальный RFC в декабре 2020 года (RFC #188, Abramov et al.) и стабилизированные в React 19 (декабрь 2024), - наиболее значимый архитектурный сдвиг в модели компонентов React со времён введения хуков в React 16.8. Центральный тезис: RSC - это не техника оптимизации поверх существующей модели, а новая парадигма рендеринга, переопределяющая единицу композиции, локус загрузки данных и контракт между сервером и клиентом. Анализ состоит из четырёх частей: (1) таксономия рендеринга до RSC, (2) формальная спецификация модели RSC и wire-формата, (3) операционные паттерны - async-компоненты, Suspense-стриминг, Partial Prerendering, (4) продакшн-фреймворк принятия решений с эмпирическими бенчмарками.
Часть I - Таксономия рендеринга до RSC и её недостатки
До RSC React-приложения работали в четырёх режимах рендеринга, каждый - компромисс по двум осям: где генерируется HTML (сервер vs клиент) и когда загружаются данные (этап билда, запрос или клиентская сторона).
- CSR: 100% JS-фреймворка и бизнес-логики отправляется клиенту. Пользователь видит пустую страницу до парсинга, компиляции и гидратации бандла. TTI прямо пропорционален размеру бандла.
- SSR: браузер получает HTML сразу (хорошо для LCP), но всё равно должен скачать полный React-бандл и гидрировать DOM. JavaScript-стоимость идентична CSR.
- SSG: HTML пре-рендерится при билде. Нулевые серверные вычисления на запрос. Ограничение - свежесть данных.
- ISR: Next.js-расширение SSG с управляемой перегенерацией. По-прежнему отправляет полный бандл гидратации.
Общий недостаток: клиент обязан распарсить и выполнить полный JS каждого компонента - независимо от наличия интерактивности. RSC делает разделение сервер/клиент первоклассным архитектурным примитивом.
Часть II - Формальная модель RSC: RFC 188, типы компонентов и wire-формат
RFC #188 (https://github.com/reactjs/rfcs/pull/188) определяет два взаимоисключающих типа компонентов. Классификация определяется на уровне модуля - это статическое свойство кодовой базы, верифицируемое бандлером.
Server Component - любой компонент без 'use client'. Выполняется исключительно на сервере. Может быть async-функцией, await'ить запросы к БД прямо в теле рендера. Вклад в клиентский бандл - ноль байт. Вывод - RSC wire-формат: JSON-подобный потоковый протокол (Content-Type: text/x-component).
Client Component - компонент с 'use client'. Включается в бандл, поддерживает state, effects, браузерные API. Ключевой инсайт: `'use client'` - объявление границы, а не описание места рендеринга. Client Components рендерятся на сервере при SSR; директива означает «этот компонент гидрируется и перерендеривается на клиенте». Server Components на клиенте не перерендериваются никогда.
- Ограничение сериализации: пропсы через границу сервер/клиент должны быть сериализуемы - примитивы, plain objects, Dates, Maps, Sets, Promises. Функции, экземпляры классов - нельзя.
- React Flight protocol: потоковый протокол RSC. Поддерживает внеочерёдную доставку чанков - медленный компонент не блокирует быстрых соседей.
- Паттерн 'пончика':
<ClientShell><ServerContent /></ClientShell>- валидно.ClientShellбандлится;ServerContent- нет. Клиент видит только выводServerContent, никогда его исходный код.
Часть III - Async-компоненты и архитектура загрузки данных
RSC делает Server Components async-функциями. Компонент может напрямую await'ить ORM-запрос, fetch(), чтение файловой системы в теле рендера. React-рантайм ожидает Promise перед стримингом вывода. Это намеренная модель, формализованная в RFC 188.
- Предотвращение водопадов:
Promise.all([fetchA(), fetchB()])для смежных данных внутри одного компонента устраняет искусственную последовательность. - Кеш-дедупликация: Next.js автоматически объединяет идентичные
fetch()в рамках одного рендер-прохода - несколько компонентов могут независимо вызывать один URL без N запросов. - Контекст запроса:
headers(),cookies(),paramsдоступны из любого компонента дерева, не только с корня страницы.
Suspense, стриминг и прогрессивный рендеринг
Когда async Server Component ожидает Promise, React рендерит ближайший <Suspense fallback>. По мере разрешения завершённое поддерево стримится клиенту как новый RSC-чанк. Транспорт - chunked HTTP/1.1 или HTTP/2 multiplexing. Статическая оболочка (навигация, LCP-изображения) приходит в первом чанке за 50–100мс. LCP-следствие: страница продукта отдаёт статику менее чем за 200мс FCP, пока персонализированный контент стримится в Suspense-чанках независимо.
Partial Prerendering: унифицированная статическая/динамическая модель
PPR (стабилизирован в Next.js 15) расширяет RSC-стриминг на CDN. Статическая оболочка пре-рендерится при билде и кешируется на edge - CDN отдаёт её мгновенно с нулевыми серверными вычислениями. Динамические RSC-«дыры» в <Suspense> стримятся с origin по тому же HTTP/2-соединению. По бенчмаркам Vercel 2025: 40–65% снижение TTFB на страницах с полным SSR.
Часть IV - Бенчмарки: бандл и производительность
- facebook.com (React Conf 2024): миграция data-display дерева в Server Components - сокращение клиентского бандла на 78%. ~120 КБ логики устранено из бандла; на клиент передаётся ~8 КБ RSC payload.
- Shopify Hydrogen 2 (октябрь 2024): страница продукта: JS сокращён с 340 КБ до 89 КБ. TTI на Moto G Power: 4,2с → 1,8с.
- Vercel Commerce (январь 2025): 67 КБ начального JS против 210 КБ для Pages Router - снижение на 68%.
- HTTP Archive 2025: App Router-сайты: медиана 180 КБ JS против 390 КБ для Pages Router-эквивалентов.
React 19 Compiler: синергия с RSC
React 19 Compiler автоматически вставляет мемоизацию на Client Components - эквивалент useMemo, useCallback, React.memo. Работает исключительно на Client Components. Синергия: RSC сокращает *объём* клиентского кода, Compiler - *частоту* перерендеров оставшегося. По бенчмаркам React-команды (React Conf 2024): включение Compiler на уже мемоизированном вручную приложении дополнительно сократило перерендеры на 22%.
Антипаттерны RSC
- Чрезмерный `'use client'`: правильный дефолт - Server Component везде, где нет явной потребности в state/effects/браузерных API.
- RSC для мутаций: мутации - домен Server Actions, React Query или SWR. RSC - только read-heavy render-time данные.
- Большие сериализованные пропсы: фильтруйте и пагинируйте данные до пересечения границы сервер/клиент.
- Отсутствие Suspense: каждый async Server Component должен быть обёрнут в
<Suspense fallback={<Skeleton />}>.
Фреймворк принятия решений: Server vs Client Component
- Использует
useState,useReducer,useContext? → Client Component. - Использует
useEffect,useLayoutEffect? → Client Component. - Прикрепляет DOM-обработчики (
onClick,onChange)? → Client Component. - Обращается к
window,document,localStorage,IntersectionObserver? → Client Component. - Импортирует библиотеку с любым из вышеперечисленного? → Client Component (или вынесите вызов в листовой Client Component).
- Ничего из вышеперечисленного? → Server Component - загружайте данные напрямую, вклад в бандл - ноль байт.
Кейс: страница продукта в eCommerce
- Page root (RSC, async): fetches product, renders static structure. PPR-кешируется на edge.
- `<PriceBlock>` (RSC, async, Suspense): загружает user-specific цены. Стримится независимо. Ноль байт в бандле.
- `<InventoryBadge>` (RSC, async, Suspense): живой инвентарь. Стримится параллельно с ценами.
- `<AddToCartButton>` (Client Component):
useState+ Server Action. Единственная'use client'-граница на странице. - `<RecommendationCarousel>` (RSC, async, Suspense): ML-рекомендации server-side.
Результат: ~40–60 КБ клиентского JS вместо типичных 300–500 КБ. LCP стримится в первом чанке. INP кнопки «В корзину» не зависит от сложности остальной страницы - этих компонентов нет в клиентском бандле.
Заключение
RSC меняет фундаментальный вопрос с «как сделать JS быстрее?» на «как обеспечить, чтобы на клиент отправлялся только JS, который действительно должен там выполняться?». Данные - 68–78% сокращение бандла, TTI < 2с, 40–65% снижение TTFB с PPR - подтверждают теоретическую модель. В связке с React 19 Compiler и PPR Next.js 15 представляет наиболее производительную React-архитектуру деплоя из доступных.
Для архитектурного ревью или миграции с Pages Router - услуга архитектуры фронтенда | кейсы | обсудить задачу.
Источники
- Abramov, D. et al. React RFC #188 (декабрь 2020): https://github.com/reactjs/rfcs/pull/188
- React Team. React 19 Release Notes: https://react.dev/blog/2024/12/05/react-19
- React Team. RSC Documentation: https://react.dev/reference/rsc/server-components
- Next.js Team. App Router Docs: https://nextjs.org/docs/app
- Next.js Team. Partial Prerendering: https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering
- Vercel. 'Partial Prerendering' (2025): https://vercel.com/blog/partial-prerendering
- Shopify Engineering. 'Hydrogen 2 RSC Benchmarks' (октябрь 2024): https://shopify.engineering
- HTTP Archive 2025 Web Almanac: https://almanac.httparchive.org
- React Conf 2024, React Compiler deep-dive: https://conf.react.dev
- Osmani, A. 'Patterns for Building React Applications' (2025): https://patterns.dev
