Что такое Server Components в React?

SeniorReact · Frontend·Обновлено 19 июля 2026
Коротко
Server Components — это компоненты React, которые рендерятся исключительно на сервере и никогда не попадают в клиентский JavaScript-бандл, что позволяет напрямую обращаться к базам данных, файловой системе и другим серверным ресурсам без передачи лишнего кода на клиент.

Server Components в React

Server Components (RSC) — архитектурная возможность React, стабилизированная в React 19, позволяющая выполнять рендеринг компонентов целиком на сервере. Результат рендеринга — не HTML-строка (как в SSR), а специальный сериализуемый формат React-дерева, который стримится на клиент и встраивается в существующее дерево без перезагрузки страницы.

Ключевые отличия от обычных компонентов

  • Нет клиентского JS: код Server Component не включается в бандл браузера — значит, тяжёлые библиотеки (парсеры, ORM, SDK) остаются только на сервере.
  • Прямой доступ к серверным ресурсам: можно делать async/await запросы к БД, читать файлы, обращаться к внутренним API прямо внутри компонента.
  • Нет состояния и эффектов: useState, useEffect, обработчики событий — недоступны. Для интерактивности нужны Client Components.
  • Стриминг: RSC поддерживают стриминг через <Suspense>, что позволяет показывать части UI сразу, не дожидаясь всех данных.

Разграничение Server / Client

По умолчанию в Next.js App Router все компоненты — серверные. Чтобы сделать компонент клиентским, нужна директива 'use client' в начале файла.

// ServerComponent — рендерится только на сервере
async function UserProfile({ userId }: { userId: string }) {
  // Прямой запрос к БД — безопасно, не попадёт в браузер
  const user = await db.user.findUnique({ where: { id: userId } });

  return (
    <div>
      <h2>{user.name}</h2>
      {/* Клиентский компонент встроен внутрь серверного */}
      <LikeButton postId={userId} />
    </div>
  );
}

Граница Server/Client и паттерн «дырки»

Client Component не может импортировать Server Component напрямую, но Server Component может передавать другие Server Components как children в Client Component — это называется паттерном «дырок» (slots / children composition).

// ClientWrapper.tsx
'use client';
export function ClientWrapper({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(false);
  return <div onClick={() => setOpen(!open)}>{children}</div>;
}

// Page.tsx — серверный файл
import { ClientWrapper } from './ClientWrapper';
import { ServerData } from './ServerData'; // серверный компонент

export default function Page() {
  return (
    <ClientWrapper>
      <ServerData /> {/* допустимо — передаём как children */}
    </ClientWrapper>
  );
}

Сериализуемые пропсы

Пропсы, передаваемые из Server Component в Client Component, должны быть сериализуемыми (JSON-совместимыми). Функции, классы, Set, Map — не сериализуются и вызовут ошибку.

Преимущества для производительности

  1. Меньше бандл — тяжёлые зависимости остаются на сервере.
  2. Нет водопада клиентских запросов — данные фетчатся параллельно на сервере.
  3. Автоматическое кэширование — Next.js добавляет кэш на уровне fetch.
  4. Лучший FCP/LCP — контент доступен сразу, без ожидания гидрации.

Ограничения

  • Нельзя использовать хуки состояния и эффектов.
  • Нельзя подписываться на браузерные события.
  • Нельзя использовать Context напрямую (только через Client Component-обёртки).

Что хочет услышать интервьюер

Понимание разницы между RSC и традиционным SSR: RSC возвращает не HTML, а сериализованное React-дерево, которое можно патчить без перезагрузки страницы

Знание ограничений Server Components: отсутствие useState, useEffect, обработчиков событий и необходимость директивы 'use client' для интерактивных частей

Понимание паттерна композиции (children/slots) для совмещения серверных и клиентских компонентов без нарушения границ

Осознание выгод: сокращение JS-бандла, прямой доступ к серверным ресурсам, параллельный фетчинг данных на сервере

Понимание требования сериализуемости пропсов при передаче данных из Server в Client Component

Пример: Серверный компонент с прямым запросом к БД

// app/users/page.tsx — серверный компонент по умолчанию в App Router
import { db } from '@/lib/db';

export default async function UsersPage() {
  // Этот код выполняется только на сервере — БД-credentials не утекут в браузер
  const users = await db.user.findMany({
    select: { id: true, name: true, email: true },
    take: 20,
  });

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          {user.name} — {user.email}
        </li>
      ))}
    </ul>
  );
}

Пример: Граница Server/Client и паттерн children

'use client';
// components/Collapsible.tsx — клиентский компонент с состоянием
import { useState } from 'react';

export function Collapsible({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(true);

  return (
    <section>
      <button onClick={() => setOpen((v) => !v)}>
        {open ? 'Свернуть' : 'Развернуть'}
      </button>
      {open && children}
    </section>
  );
}

// app/dashboard/page.tsx — серверный компонент-оркестратор
import { Collapsible } from '@/components/Collapsible';
import { RevenueChart } from './RevenueChart'; // тоже серверный

export default async function DashboardPage() {
  // Данные фетчатся параллельно на сервере
  const [stats, revenue] = await Promise.all([
    fetchStats(),
    fetchRevenue(),
  ]);

  return (
    <Collapsible>
      {/* Server Component передаётся как children — допустимо */}
      <RevenueChart data={revenue} />
    </Collapsible>
  );
}

Типичные ошибки

Путать RSC с SSR: думать, что Server Components — это просто компоненты, отрендеренные в HTML на сервере, тогда как RSC имеют особый wire-формат и поддерживают стриминг и частичные обновления

Пытаться импортировать Server Component внутрь Client Component напрямую, вместо использования паттерна children/slots

Передавать несериализуемые данные (функции, классы) как пропсы из Server Component в Client Component

Добавлять 'use client' везде «на всякий случай», теряя все преимущества архитектуры и увеличивая бандл

Не понимать, что контекст React (Context API) недоступен в Server Components — для глобального состояния нужны Client Component-обёртки

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее