SSR: Server Side Rendering

Server Side Rendering (SSR) — стратегия рендеринга, при которой сервер генерирует полный HTML-документ при каждом запросе и отправляет его браузеру; клиент затем «оживляет» страницу через процесс hydration.

Зачем нужно

Чистый CSR отдаёт браузеру пустой HTML — поисковые роботы и пользователи видят контент только после загрузки и выполнения JS. SSR решает эту проблему: первый байт HTML уже содержит готовый контент, FCP (First Contentful Paint) быстрый, SEO работает нативно. После загрузки JS происходит hydration — React «привязывается» к серверному HTML и дальше приложение работает как SPA.

Где используется

  • E-commerce (страницы товаров с SEO, актуальными ценами и остатками)
  • Новостные сайты и медиа-платформы
  • Приложения с персонализированным контентом на основе сессии пользователя
  • Любые публичные страницы, где важны SEO и быстрый FCP

Как работает SSR

Запрос GET /product/42
      ↓
Next.js сервер
      ↓
getServerSideProps: загружает данные о товаре (БД/API)
      ↓
React рендерит компонент в HTML-строку (renderToString)
      ↓
Сервер отправляет полный HTML + данные (JSON) в `__NEXT_DATA__`
      ↓
Браузер: мгновенно показывает HTML (быстрый FCP)
      ↓
Браузер загружает bundle.js
      ↓
Hydration: React читает `__NEXT_DATA__`, привязывается к DOM
      ↓
Страница интерактивна (TTI)

Next.js Pages Router: getServerSideProps

// pages/product/[id].jsx

// Вызывается при КАЖДОМ запросе на сервере
export async function getServerSideProps(context) {
  const { params, req, res } = context;

  // Доступ к cookies для персонализации
  const userId = req.cookies['user-id'];

  // Загрузка данных при каждом запросе
  const [product, recommendations] = await Promise.all([
    fetchProduct(params.id),
    fetchRecommendations(params.id, userId),
  ]);

  if (!product) {
    return { notFound: true };
  }

  // Кэширование на уровне HTTP
  res.setHeader('Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59');

  return {
    props: {
      product,
      recommendations,
    },
  };
}

export default function ProductPage({ product, recommendations }) {
  return (
    <main>
      <h1>{product.name}</h1>
      <p>{product.price} ₽</p>
      <RecommendationsList items={recommendations} />
    </main>
  );
}

Next.js App Router: Server Components (новый SSR)

// app/product/[id]/page.tsx
// В App Router SSR — это просто async Server Component

async function ProductPage({ params }: { params: { id: string } }) {
  const product = await fetch(`https://api/products/${params.id}`, {
    // no-store = всегда свежие данные (SSR-поведение)
    cache: 'no-store',
  }).then(r => r.json());

  return (
    <main>
      <h1>{product.name}</h1>
      <p>{product.price} ₽</p>
    </main>
  );
}

Hydration mismatch — частая проблема

// Эта ошибка возникает когда сервер и клиент рендерят разное
function RandomComponent() {
  // Math.random на сервере даёт одно значение,
  // на клиенте при hydration — другое → mismatch error
  const id = Math.random; // НЕ ДЕЛАЙТЕ ТАК

  // Правильно: используйте useId или фиксированное значение
  const id = React.useId;
  return <div id={id}>...</div>;
}

Частые ошибки

  • Hydration mismatch — HTML от сервера отличается от того, что React генерирует на клиенте; типичные причины: Math.random, Date.now(), браузерные API в рендере.
  • Слишком много данных в SSR — загружают всю страницу через SSR, хотя только шапка нужна серверу; остальное лучше загружать на клиенте.
  • Нет кэширования HTTP-заголовков — каждый запрос к SSR-странице создаёт нагрузку на сервер; настраивайте Cache-Control для частично-кэшируемого контента.

Связанные темы

Ресурсы