Hydration в React: что происходит после SSR и где ломается интерактивность

Разбираем hydration в React после SSR: как браузер связывает HTML с деревом React, почему возникают hydration mismatch, сколько стоит гидрация и как уменьшить клиентскую нагрузку.

11 марта 2026 г.18 минLexicon Team

Введение

Когда говорят про SSR в React, разговор часто обрывается на фразе «сервер отдал HTML быстрее». Это только половина картины. После того как браузер получил HTML, начинается этап, который и определяет, когда страница станет по-настоящему живой. Этот этап называется hydration.

Если коротко, hydration в React после SSR означает, что клиентский React не рисует экран с нуля, а пытается взять уже существующую HTML-разметку, сопоставить ее со своим деревом компонентов и подключить интерактивность. Именно здесь появляются типичные production-проблемы: hydration mismatch, длинные блокировки main thread, мигание UI, повторные запросы и ощущение «контент виден, но страница еще не работает».

Тема всплывает и в архитектурных обсуждениях, и на собеседованиях middle/senior уровня. Особенно сейчас, когда рядом существуют SSR, RSC, Suspense и concurrent-механика React. Для общего контекста полезно держать рядом разбор SSR vs CSR vs RSC: он помогает не путать доставку HTML, серверные компоненты и сам процесс гидрации.

Больше вопросов в Telegram

Ежедневные разборы и реальные кейсы с интервью.

Подписаться

Что такое hydration без упрощений

Hydration - это не «еще один render». Сервер уже выполнил render и отдал HTML. Теперь задача React в браузере другая:

  1. разобрать клиентский bundle;
  2. построить клиентское дерево React для текущего экрана;
  3. сопоставить это дерево с уже существующим DOM;
  4. привязать обработчики событий;
  5. подготовить дерево к дальнейшим обновлениям без полной пересборки страницы.

Важный нюанс: браузер может показать HTML до завершения hydration. Поэтому у SSR-страницы есть как минимум два разных момента готовности:

  • контент виден;
  • интерфейс интерактивен.

Именно путаница между этими двумя состояниями рождает много слабых выводов вроде «SSR всегда быстрый». Нет. Он может дать ранний первый экран, но если гидрация тяжелая, пользователь увидит кнопку раньше, чем сможет по ней нажать.

Что происходит после SSR по шагам

1. Браузер получает HTML и начинает парсинг

На первом этапе пользователь видит серверный HTML. Для контентных страниц это уже большой выигрыш: текст, заголовки, изображения и базовая структура появляются раньше, чем при чистом CSR. Но этот HTML пока пассивен.

2. Загружается клиентский JavaScript

Дальше браузер скачивает, парсит и исполняет клиентские чанки. На слабом устройстве именно здесь часто лежит узкое место. Если bundle раздут, выигрыш от SSR частично съедается parse/execute cost. По этой причине разговор о гидрации почти всегда связан с размером клиентской сборки и границами "use client" в server-first архитектуре. Эта связка подробно разобрана в статье про Server Components.

3. React строит клиентское дерево и пытается его привязать к DOM

React вызывает hydrateRoot(...), получает корневой контейнер и начинает сравнивать ожидаемую структуру со страницей, которую уже отрисовал сервер.

import { hydrateRoot } from "react-dom/client";
import { App } from "./App";

hydrateRoot(document.getElementById("root")!, <App />);

Смысл здесь не в том, чтобы заново создать весь DOM, а в том, чтобы переиспользовать уже существующие узлы, если серверный и клиентский вывод совпадают.

4. Подключаются события и состояние

После успешного сопоставления React начинает «оживлять» интерфейс: клики, ввод, локальное состояние, эффекты, подписки. В этот момент страница переходит из режима «статичная, но видимая» в режим «интерактивная и управляемая React».

5. Дерево становится обычным клиентским React-деревом

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

Архитектурный разбор: где именно находится гидрация в общем pipeline

Для production удобнее думать о странице как о цепочке из пяти стадий:

СтадияГде выполняетсяЧто получает пользовательГлавное узкое место
Data fetching для SSRсерверпока ничегоlatency до API и базы
Server renderсерверпока ничегоCPU сервера и шаблонный render
HTML responseсеть + браузервидимый контентTTFB и размер ответа
Hydrationбраузерпереход к интерактивностиparse/execute JS и main thread
Последующие updatesбраузеробычный React UIлокальные ререндеры и эффекты

Практический вывод простой: SSR отвечает за ранний HTML, а hydration отвечает за переход к интерактивности. Это разные точки оптимизации и разные точки отказа.

На интервью сильный ответ обычно звучит так: «Я отдельно оцениваю стоимость серверного рендера и стоимость hydration. Быстрый первый HTML не означает быстрый usable UI». Именно эта формулировка обычно отделяет инженерный ответ от пересказа документации.

Базовый пример: сервер и клиент совпадают

Если сервер и клиент строят один и тот же JSX из одних и тех же данных, hydration проходит спокойно.

type ProductProps = {
  title: string;
  price: number;
};

export function ProductCard({ title, price }: ProductProps) {
  return (
    <article>
      <h2>{title}</h2>
      <p>{price} ₽</p>
      <button>Купить</button>
    </article>
  );
}

Сервер отдаст HTML карточки, а клиент потом подключит обработчик кнопки, если он есть в реальном дереве. Здесь важен не сам JSX, а детерминированность вывода: одинаковые входные данные дают одинаковую разметку на сервере и клиенте.

Почему возникает hydration mismatch

Hydration mismatch появляется, когда React ожидает один DOM, а в контейнере уже лежит другой. Типовые причины почти всегда повторяются.

1. Нестабильные значения в рендере

Самый частый антипример: использовать Date.now(), Math.random() или генерацию id прямо в JSX во время первого рендера.

export function BadClock() {
  return <time>{Date.now()}</time>;
}

На сервере и клиенте значения почти гарантированно будут разными. Итог: warning, повторная пересборка участка, иногда визуальный дерг.

2. Условный JSX по browser API

export function BadThemeLabel() {
  const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
  return <span>{isDark ? "dark" : "light"}</span>;
}

Такой код вообще не должен участвовать в server render. На сервере window нет, а если обойти это условием, можно получить разный первичный вывод на клиенте и сервере.

3. Разные данные на сервере и клиенте

Если сервер отрендерил список из одного набора данных, а клиент сразу после загрузки сделал re-fetch и получил другой результат, интерфейс может мигнуть или пересобраться прямо в hydration-фазе.

4. Некорректная работа с формами

Формы особенно чувствительны к расхождению между defaultValue и value. Отдельно это разобрано в статье про формы, SSR и hydration.

Как исправлять mismatch без костылей

Рабочее правило: все, что влияет на первый render, должно быть детерминированным и одинаковым на сервере и клиенте.

Плохой вариант:

export function PromoBanner() {
  const variant = Math.random() > 0.5 ? "A" : "B";
  return <section>{variant}</section>;
}

Лучше так:

type PromoBannerProps = {
  variant: "A" | "B";
};

export function PromoBanner({ variant }: PromoBannerProps) {
  return <section>{variant}</section>;
}

Теперь решение о варианте принимается вне рендера: на сервере, в data-layer или через заранее переданный проп. Это важно не только для корректности, но и для отладки. Когда variant вычисляется детерминированно, команда видит источник истины, а не случайное поведение в браузере.

Selective hydration: почему React не обязан оживлять все строго по очереди

В React 18+ гидрация стала гибче. Если раньше тему объясняли как «весь SSR-экран постепенно становится интерактивным», то на практике React умеет приоритизировать участки дерева.

Смысл selective hydration такой:

  • если пользователь уже взаимодействует с конкретным участком;
  • если рядом есть границы Suspense;
  • если часть дерева готова раньше другой;

React может активировать более важные зоны раньше, не дожидаясь полной гидрации всего экрана.

Это особенно полезно для длинных страниц, где header, навигация и поиск важнее, чем тяжелый вторичный виджет ниже по странице. Здесь hydration тесно связана с тем, как вы расставляете Suspense-границы и как делите экран на интерактивные острова. Детально этот механизм дополняет разбор Suspense в React и статья про Concurrent Rendering.

Сравнение стратегий: CSR, SSR + hydration, SSR + узкие client islands

КритерийCSRSSR + hydration всего дереваSSR + узкие client islands
Первый HTMLпозднийраннийранний
Стоимость JS на клиентевысокаясредняя или высокаяниже
Цена гидрациинет отдельной, но есть полный client renderчасто заметнаяниже при узких границах
Риск mismatchнизкийсреднийсредний, но зона меньше
SEOслабеехорошийхороший
Подходит длязакрытых интерактивных приложенийпубличных страниц со средним UIконтентных страниц и server-first экранов

Ключевая мысль по таблице: проблема обычно не в самом SSR, а в том, что команда после SSR продолжает отправлять слишком много клиентского кода и гидратирует лишнее дерево.

Production pitfalls: где гидрация чаще всего становится дорогой

1. Слишком высокий "use client"

Если клиентской границей помечают layout или крупный route-level контейнер без необходимости, в браузер уезжает слишком большой кусок дерева. В результате HTML пришел рано, но потом вся страница долго «оживает».

2. Тяжелые эффекты на старте

Даже если hydration формально завершилась без mismatch, ранние useEffect могут сразу занять main thread: аналитика, listeners, re-measure layout, повторные fetch, синхронные вычисления.

3. Повторный fetch тех же данных

Если сервер уже потратил ресурсы на данные для SSR, а клиент при mount тут же запрашивает их заново без причины, команда платит дважды: сервером и стартовой клиентской работой.

4. Неправильные ключи и нестабильное дерево

Если структура списка на сервере и клиенте не совпадает по key, React получает лишнюю сложность прямо в момент hydration. Базовая механика идентичности элементов подробно разбирается в материале про ошибки с key.

Разбор производительности: что именно измерять

Когда обсуждают hydration, полезно отделять три разных вопроса:

  1. Насколько быстро пришел HTML?
  2. Насколько быстро JavaScript стал готов к работе?
  3. Когда пользователь реально может взаимодействовать с экраном без лагов?

Минимальный набор метрик:

  • TTFB для серверного ответа;
  • LCP для появления основного контента;
  • размер клиентского bundle и стоимость parse/execute;
  • long tasks на main thread в момент старта;
  • задержка до первой осмысленной интерактивности.

Если после SSR у вас хороший LCP, но кнопки и фильтры несколько секунд не отвечают, проблема почти наверняка уже не в серверном рендере, а в гидрации и стартовом клиентском коде.

Практики, которые обычно работают лучше

Архитектурные практики

  • Проектируйте экран по принципу server first, а не client by default.
  • Держите read-only блоки на серверной стороне, если им не нужны события и локальное состояние.
  • Делайте интерактивные острова узкими: форма поиска, фильтр, кнопка лайка, чат-виджет, но не весь экран целиком.

Практики кода

  • Не вычисляйте нестабильные значения в первом рендере.
  • Не обращайтесь к browser API внутри server render-пути.
  • Не запускайте лишний re-fetch сразу после hydration без проверяемой причины.

Практики наблюдаемости

  • Логируйте hydration warnings в staging и не игнорируйте их как «шум».
  • Снимайте профили на слабых устройствах, а не только на машине разработчика.
  • Разделяйте в метриках серверную latency и клиентскую стоимость старта.

Rollout и rollback

  • Миграцию на server-first схему делайте поэтапно, а не одним релизом.
  • Держите feature flag на спорных client boundaries.
  • Сравнивайте размер bundle и время интерактивности до и после изменений, а не только субъективное ощущение команды.

Прокачай React за 7 дней

20 вопросов и разборов по React Hooks.

Начать

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

  1. Считать, что ранний HTML автоматически означает быстрый интерфейс.
  2. Путать SSR и hydration, как будто это один и тот же этап.
  3. Лечить mismatch через suppressHydrationWarning, не исправив первопричину.
  4. Дублировать data fetching на сервере и клиенте без явной стратегии.
  5. Поднимать "use client" слишком высоко и потом удивляться тяжелой гидрации.
  6. Оценивать только LCP, игнорируя лаги после появления контента.

Как отвечать на интервью

Рабочая структура ответа на вопрос «что происходит после SSR в React?» выглядит так:

  1. Сервер отдает HTML, и пользователь может увидеть контент раньше.
  2. Затем браузер загружает клиентский JavaScript.
  3. React запускает hydration: сопоставляет клиентское дерево с готовым DOM и подключает обработчики событий.
  4. Если серверный и клиентский вывод расходятся, возникает hydration mismatch.
  5. Главное ограничение SSR не в HTML, а в цене последующей гидрации и клиентского bundle.

Пример сильного ответа:

После SSR браузер уже может показать HTML, но страница еще не интерактивна. React загружает клиентский код, вызывает hydration, сопоставляет свое дерево с существующим DOM и подключает события. Если серверный и клиентский render отличаются, возникают hydration mismatch и лишняя пересборка. Поэтому я оцениваю не только TTFB и LCP, но и стоимость hydration, размер client bundle и границы client components.

Такой ответ показывает механизм, точку отказа и инженерные компромиссы. Слабый ответ обычно звучит короче: «после SSR React просто подключает события». Формально не ложь, но глубины в нем почти нет.

Практика React-собеседований по рендерингу и архитектуре

Разберите реальные вопросы по SSR, hydration, RSC, производительности и границам client/server в формате mock interview и коротких инженерных разборов.

Начать подготовку

FAQ

Что такое hydration в React простыми словами?

Это этап после SSR, когда React в браузере берет готовый HTML, сверяет его со своим деревом и делает страницу интерактивной: подключает события, состояние и дальнейшие обновления.

Почему возникают hydration mismatch?

Почти всегда из-за расхождения между серверным и клиентским выводом: случайные значения, даты, чтение window в рендере, разные данные или нестабильная структура JSX.

SSR всегда ускоряет страницу?

Нет. SSR ускоряет появление первого HTML, но не гарантирует быструю интерактивность. Если bundle большой и гидрация дорогая, интерфейс может быть видимым, но еще долго оставаться «полуживым».

Чем selective hydration отличается от обычной?

React может не ждать полной активации всего дерева, а гидратировать более важные участки раньше, особенно если пользователь уже с ними взаимодействует или если дерево разбито на границы Suspense.

Как уменьшить стоимость hydration?

Сужать клиентские границы, не отправлять лишний JavaScript, переносить read-only блоки в серверный слой, избегать тяжелых клиентских эффектов на старте и следить за повторным fetch после SSR.

Итоги

Hydration в React - это не второстепенная деталь после SSR, а критическая часть pipeline. Сервер дает ранний HTML, но именно гидрация решает, когда страница станет управляемой и отзывчивой.

Если смотреть на тему инженерно, главный вопрос не «есть ли у нас SSR», а «сколько клиентской работы остается после SSR и насколько она дорога». От этого зависят и реальная производительность, и архитектурные решения вокруг RSC, Suspense, client islands и размера bundle.

Больше вопросов в Telegram

Ежедневные разборы и реальные кейсы с интервью.

Подписаться

Автор

Lexicon Team

Читайте также