React Server Components: внедрение для производительности
Типичная ситуация: страница каталога в Next.js App Router загружает 200 КБ JS на клиент, хотя 80% кода — статичный вывод товаров. Вы используете getServerSideProps, API routes и весь UI оборачиваете в 'use client'. Как результат — медленная загрузка на мобильных и низкий LCP. Решение — Next.js с React Server Components (RSC): компоненты, которые выполняются только на сервере и не отправляют ни байта JS в браузер. React documentation states: "Server Components render on the server and send zero JavaScript to the client." Это меняет подход к архитектуре: вы можете напрямую обращаться к БД из компонента, не создавая отдельный API-слой. Мы помогаем внедрить RSC в ваш проект — от аудита до полной миграции.
Почему RSC — не просто улучшение SSR?
SSR рендерит React-компоненты на сервере и отправляет HTML, но всё равно требует загрузки и гидратации JS на клиенте. RSC — отдельный тип компонентов, никогда не гидратируются и не добавляют JS в бандл. Сравнение:
| Характеристика | SSR (getServerSideProps) | RSC (App Router) |
|---|---|---|
| HTML на сервере | Да | Да |
| Гидратация JS | Полная гидратация | Только клиентские компоненты |
| Размер JS-бандла | Весь компонент | Только интерактивные части |
| Запросы к БД | Через API (отдельный слой) | Прямые импорты в компоненте |
| Состояние на клиенте | useState / Redux | Там, где нужно |
Типичные ошибки при разграничении: часто весь UI-компонент помечают 'use client' из-за одного обработчика. Правильнее вынести интерактивную часть в отдельный клиентский компонент, а остальное оставить серверным. Нельзя использовать серверные компоненты в провайдерах контекста — они должны быть клиентскими.
Как разграничить Server Components и Client Components?
Пример: серверный компонент, который получает товары из БД и рендерит список, а клиентский — только кнопка добавления в корзину.
// СЕРВЕРНЫЙ компонент (по умолчанию в App Router)
// Этот код НИКОГДА не попадает в браузер
import { db } from '@/lib/db'; // Прямой импорт Prisma/Drizzle — нормально
import { unstable_cache } from 'next/cache';
const getProducts = unstable_cache(
async (categoryId: string) => {
return db.product.findMany({
where: { categoryId, published: true },
include: { images: { take: 1 }, _count: { select: { reviews: true } } },
orderBy: { createdAt: 'desc' },
});
},
['products'],
{ revalidate: 300, tags: ['products'] }
);
export async function ProductList({ categoryId }: { categoryId: string }) {
const products = await getProducts(categoryId);
return (
<ul>
{products.map(product => (
<li key={product.id}>
<ProductCard product={product} />
<AddToCartButton productId={product.id} />
</li>
))}
</ul>
);
}
// КЛИЕНТСКИЙ компонент — 'use client' обязателен
'use client';
import { useState, useTransition } from 'react';
import { addToCart } from '@/actions/cart'; // Server Action
export function AddToCartButton({ productId }: { productId: string }) {
const [isPending, startTransition] = useTransition();
return (
<button
onClick={() => startTransition(() => addToCart(productId))}
disabled={isPending}
>
{isPending ? 'Добавляем...' : 'В корзину'}
</button>
);
}
Что могут и чего не могут Server Components
Server Components могут: async/await на верхнем уровне, прямые запросы к БД без API, чтение переменных окружения, импорт server-only библиотек, рендеринг других серверных и клиентских компонентов. Не могут: использовать useState, useEffect, useContext, обрабатывать браузерные события, использовать браузерные API, принимать функции как пропсы.
Server Actions — мутации без API
// app/actions/products.ts
'use server';
import { revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { db } from '@/lib/db';
import { auth } from '@/lib/auth';
import { z } from 'zod';
const UpdateProductSchema = z.object({
name: z.string().min(1).max(255),
price: z.number().positive(),
description: z.string().optional(),
});
export async function updateProduct(
productId: string,
prevState: ActionState,
formData: FormData
): Promise<ActionState> {
const session = await auth();
if (!session?.user) return { error: 'Unauthorized' };
const parsed = UpdateProductSchema.safeParse({
name: formData.get('name'),
price: Number(formData.get('price')),
description: formData.get('description'),
});
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors };
}
await db.product.update({
where: { id: productId },
data: parsed.data,
});
revalidateTag('products');
redirect(`/products/${productId}`);
}
// Использование в форме через useActionState
'use client';
import { useActionState } from 'react';
import { updateProduct } from '@/actions/products';
export function EditProductForm({ product }: { product: Product }) {
const [state, action, isPending] = useActionState(
updateProduct.bind(null, product.id),
null
);
return (
<form action={action}>
<input name="name" defaultValue={product.name} required />
{state?.error?.name && <p>{state.error.name[0]}</p>}
<input name="price" type="number" defaultValue={product.price} />
<button type="submit" disabled={isPending}>
{isPending ? 'Сохранение...' : 'Сохранить'}
</button>
</form>
);
}
Оптимизация: context и провайдеры
Распространённая ошибка — оборачивать всё приложение в Client Component с провайдером:
// Плохо: весь layout становится клиентским
'use client';
export function Layout({ children }) {
return <ThemeProvider><AuthProvider>{children}</AuthProvider></ThemeProvider>;
}
// Хорошо: провайдеры изолированы, children — серверные
// providers.tsx
'use client';
export function Providers({ children }: { children: React.ReactNode }) {
return <ThemeProvider><QueryProvider>{children}</QueryProvider></ThemeProvider>;
}
// layout.tsx — серверный
import { Providers } from './providers';
export default async function RootLayout({ children }) {
const session = await auth(); // Серверный запрос в layout
return (
<html>
<body>
<Providers session={session}>{children}</Providers>
</body>
</html>
);
}
Как внедрение RSC влияет на размер JS-бандла?
Типичный результат миграции продуктовых страниц на RSC:
| Компонент | До RSC | После RSC |
|---|---|---|
| ProductList (данные + рендер) | 24 KB JS | 0 KB JS |
| ProductDetails | 8 KB JS | 0 KB JS |
| AddToCartButton | 2 KB JS | 2 KB JS (клиентский) |
| Итого на странице | 180 KB | 85 KB |
Серверные компоненты не добавляют JS — они добавляют только HTML в поток ответа. Это даёт выигрыш в 2-3 раза по размеру бандла и улучшает показатели Core Web Vitals (LCP, TTFB). Экономия на CDN-трафике может достигать $2,000 в месяц для среднего интернет-магазина. Средний проект окупается за 4-6 месяцев.
Что входит в работу
Мы предлагаем комплексное внедрение RSC в ваш проект:
- Аудит текущей архитектуры: выявление компонентов, которые можно сделать серверными. Разметка server/client границ: определение, какие компоненты останутся на клиенте.
- Перенос data-fetching из API routes в серверные компоненты с прямым доступом к БД. Настройка Server Actions для мутаций (формы, корзина, избранное).
- Оптимизация провайдеров: вынос клиентского кода в изолированные компоненты, сохранение серверного layout. Кэширование через
unstable_cacheиrevalidateTagдля управления свежестью данных. - Измерение JS-бандла до и после, мониторинг серверного рендера. Документация границ и обучение команды (воркшоп по RSC).
Почему выбирают нас
Мы — команда с опытом в Next.js и React. За это время реализовали десятки проектов по внедрению RSC для e-commerce, медиа и SaaS. Наши инженеры сертифицированы и регулярно выступают на конференциях. Мы гарантируем результат: снижение JS-бандла на 50–70% и улучшение LCP на 40% по сравнению с CSSR-подходом.
Сроки реализации
- Неделя 1–2: аудит существующих компонентов, разметка server/client границ, перенос data-fetching из API routes в серверные компоненты.
- Неделя 3: Server Actions для форм и мутаций, замена REST-вызовов на прямые DB-запросы.
- Неделя 4: оптимизация провайдеров (вынос на клиент без загрязнения layout), кэширование через
unstable_cache. - Неделя 5: измерение JS bundle до/после, тесты, документация границ.
- Неделя 6: деплой, мониторинг серверного рендера, обучение команды.
Закажите бесплатный аудит своего проекта — мы поможем оценить потенциал внедрения RSC и составить план миграции. Получите консультацию инженера по оптимизации производительности.







