Что такое гидратация и как исправить ошибки гидратации
Продвинутое3 минРедакция Innovialabs
Коротко
Гидратация — это момент, когда React в браузере «оживляет» готовый серверный HTML, подключая к нему обработчики событий. Ошибка гидратации возникает, если первый рендер в браузере отличается от серверного, и лечится тем, что всё нестабильное (даты, случайные значения, window) переносится в эффект или клиентский компонент.
Что такое гидратация
При серверном рендеринге (SSR или SSG) браузер сразу получает готовый HTML: текст и картинки видны, но кнопки ещё не работают. Затем загружается JavaScript, и React проходит по этому HTML, строит у себя то же дерево компонентов и подключает обработчики событий к уже существующим элементам. Этот процесс и называется гидратацией.
Ключевое условие: React ожидает, что первый рендер на клиенте даст точно такой же результат, как на сервере. Если результат отличается, возникает ошибка гидратации (hydration mismatch).
Сколько стоит гидратация
Гидратация не бесплатна:
- браузеру нужно скачать, разобрать и выполнить JavaScript всех интерактивных компонентов;
- пока она не завершилась, страница выглядит готовой, но может не реагировать на клики;
- на слабых телефонах это заметно ухудшает отзывчивость.
Как снизить стоимость:
- Server Components (в React и Next.js App Router) не гидратируются вовсе — их JavaScript не попадает в браузер. Делайте клиентскими только интерактивные части.
- Не ставьте
"use client"наверх дерева — всё, что ниже, станет клиентским. - Ленивая загрузка тяжёлых виджетов (
dynamic(),React.lazy), которые не нужны сразу.
Типичные причины несовпадения
| Причина | Почему ломается |
|---|---|
new Date(), Date.now() | Время на сервере и в браузере разное |
Math.random(), генерация id | Разные значения при каждом рендере |
window, localStorage, navigator | На сервере их нет, ветка кода отличается |
| Форматирование дат и чисел | Разные часовые пояса и локали |
| Невалидная вложенность HTML | Например, <div> внутри <p> — браузер перестраивает разметку |
| Расширения браузера | Добавляют атрибуты или элементы до старта React |
Как исправить
1. Переносите браузерную логику в useEffect. Эффекты не выполняются на сервере, поэтому первый рендер совпадёт, а значение появится сразу после.
"use client";
import { useEffect, useState } from "react";
export function LocalTime() {
const [time, setTime] = useState<string | null>(null);
useEffect(() => {
setTime(new Date().toLocaleTimeString());
}, []);
return <span>{time ?? "—"}</span>;
}
2. Используйте useId для идентификаторов. Он выдаёт одинаковые id на сервере и клиенте, в отличие от Math.random().
3. Фиксируйте часовой пояс и локаль. Передавайте их явно в Intl.DateTimeFormat или форматируйте дату на сервере и отдавайте готовую строку.
4. Отключайте SSR для чисто клиентских виджетов. Карты, редакторы, графики, зависящие от размера окна, можно загружать только в браузере — в Next.js через dynamic(() => import(...), { ssr: false }) внутри клиентского компонента.
5. Исправьте разметку. Проверьте, нет ли блочных элементов внутри <p>, вложенных <a> или <table> без <tbody>.
6. suppressHydrationWarning — только точечно. Атрибут подавляет предупреждение для одного элемента, например для метки времени. Это не исправление, а осознанное исключение.
Как найти источник ошибки
- Читайте сообщение в консоли целиком: современный React показывает diff серверного и клиентского значения.
- Откройте страницу в режиме инкогнито без расширений, чтобы исключить их влияние.
- Сравните «исходный код страницы» (серверный HTML) с тем, что видно в инспекторе.
- Ищите в проблемном компоненте
Date,random,windowи условия вродеtypeof window !== "undefined"прямо в рендере.