CSR, SSR, SSG: обзор подходов

CSR (Client-Side Rendering), SSR (Server-Side Rendering) и SSG (Static Site Generation) — три основных стратегии рендеринга веб-приложений, определяющие где и когда генерируется HTML.

Зачем нужно

Выбор стратегии рендеринга напрямую влияет на производительность, SEO и сложность проекта. CSR подходит для внутренних инструментов без SEO-требований; SSR нужен, когда важны и SEO, и динамический контент; SSG обеспечивает максимальную скорость для редко меняющихся страниц. Понимание разницы позволяет принимать осознанные архитектурные решения на этапе проектирования, а не переделывать их позже.

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

  • CSR: дашборды, CRM, SaaS-приложения за авторизацией (Trello, Notion)
  • SSR: e-commerce, новостные сайты, публичные приложения с SEO (Next.js с getServerSideProps)
  • SSG: блоги, документация, лендинги (Astro, Next.js static export, Gatsby)
  • Гибрид: Next.js App Router позволяет использовать разные стратегии на разных маршрутах

Сравнение подходов

CSR (Client-Side Rendering)
──────────────────────────
Сервер → пустой HTML + bundle.js
Браузер → выполняет JS → строит DOM

SSR (Server-Side Rendering)
──────────────────────────
Запрос → сервер рендерит HTML с данными → отправляет браузеру
Браузер → показывает HTML → загружает JS → hydration

SSG (Static Site Generation)
────────────────────────────
npm run build → генерирует HTML-файлы → CDN
Браузер → получает готовый HTML мгновенно

Метрики сравнения

Критерий CSR SSR SSG
TTFB (Time to First Byte) Быстрый Медленнее (рендеринг на сервере) Быстрый (CDN)
FCP (First Contentful Paint) Медленный Быстрый Быстрый
SEO Сложно Нативно Нативно
Динамические данные Да Да Нет (нужен rebuild)
Масштабирование Легко (статика) Нужен сервер Легко (CDN)
Сложность Средняя Высокая Низкая

Пример: CSR (React без SSR)

// Браузер получает пустую страницу, JS строит UI
function App() {
  const [users, setUsers] = React.useState();

  React.useEffect(() => {
    fetch('/api/users')
      .then(r => r.json())
      .then(setUsers);
  }, );

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

Пример: SSR (Next.js)

// pages/users.jsx — данные загружаются на сервере при каждом запросе
export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/users');
  const users = await res.json();

  return { props: { users } };
}

export default function UsersPage({ users }) {
  // HTML уже содержит список пользователей при первой отдаче
  return (
    <ul>
      {users.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

Пример: SSG (Next.js)

// pages/blog/[slug].jsx — страницы генерируются при сборке
export async function getStaticPaths() {
  const posts = await fetchAllPosts;
  return {
    paths: posts.map(p => ({ params: { slug: p.slug } })),
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const post = await fetchPost(params.slug);
  return { props: { post } };
}

export default function BlogPost({ post }) {
  return <article><h1>{post.title}</h1><p>{post.content}</p></article>;
}

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

  • CSR для публичного сайта — Google всё лучше рендерит JS, но другие поисковики и социальные сети (og:tags) по-прежнему видят пустой HTML.
  • SSR для статических данных — запускать сервер для страниц, которые меняются раз в месяц, — избыточно; SSG + ISR справится лучше.
  • Забывают о hydration mismatch — при SSR HTML с сервера должен точно совпадать с тем, что React генерирует на клиенте; расхождения приводят к ошибкам.

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

Ресурсы