Головна
» Базові знання
»
Як виправити помилку «Hydration failed because the initial UI does not match»
Як виправити помилку «Hydration failed because the initial UI does not match»
Бажаний результат легко описати: HTML, згенерований на сервері, повинен збігатися з тим, що React створює під час першого рендерингу в браузері. Коли це так, React може прикріпити обробники подій і зробити сторінку інтерактивною без виникнення помилки невідповідності гідратації, заміни піддерева або несподіваного візуального стрибка.
Гідратація — це процес, під час якого React бере HTML, який уже був відрендерений на сервері, і прикріплює до нього поведінку React у браузері. Поточна документація hydrateRoot вказує, що вміст, відрендерений на клієнті, повинен бути ідентичним вмісту, відрендереному на сервері, а невідповідності слід вважати помилками.
Точне формулювання помилки змінювалося в різних релізах React та фреймворків. Ви можете побачити старіше повідомлення, таке як "Hydration failed because the initial UI does not match what was rendered on the server", або новіше повідомлення, яке пояснює, що дерево, відрендерене на сервері, не збіглося з клієнтським. Принцип налагодження залишається тим самим.
Контекст версії має значення. Станом на 11 вересня 2026 року офіційний сайт React вказує React 19.3 як останню версію React, тоді як поточна документація Next.js ідентифікує Next.js 16.3.4 як останній реліз Next.js. Перевіряйте сторінку версій React та поточну документацію Next.js, якщо ви читаєте це пізніше, оскільки доступні API та повідомлення про помилки можуть змінюватися.
Ілюстрація, згенерована ШІ: Почніть з пошуку першого компонента, зазначеного в помилці гідратації, і переконайтеся, що проблема виникає при новому завантаженні сторінки. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Що вважається успішним виправленням?
Не оцінюйте успіх лише за тим, чи зникло червоне накладання в середовищі розробки. Гарне виправлення повинно задовольняти кілька перевірок:
Попередження або помилка гідратації більше не з'являються при чистому перезавантаженні.
Початковий інтерфейс, відрендерений на сервері, і перший рендеринг React у браузері представляють однаковий вміст і структуру.
Зачеплений компонент залишається інтерактивним після гідратації.
Немає очевидного мерехтіння від одного значення до іншого, якщо ця зміна не є навмисною та спроектованою.
Проблема залишається виправленою у виробничій збірці, а не лише на сервері розробки.
Ви виправили причину, а не приховали реальну невідповідність за допомогою опції придушення попереджень.
Якщо попередження зникає, але сторінка тепер відображає важливий вміст лише після завантаження JavaScript, помилка може зникнути, але досвід користувача погіршиться. Це може бути прийнятним компромісом для віджета, що працює лише в браузері, але це не автоматично найкращий результат для основного вмісту сторінки.
Крок 1: Відтворіть невідповідність і знайдіть найменший компонент, що не працює
Почніть з жорсткого перезавантаження в середовищі розробки та прочитайте всю помилку, включаючи стек компонентів. Документована помилка гідратації в React 19 перелічує кілька поширених причин: розгалуження сервер/клієнт, такі як typeof window !== 'undefined', змінні значення, такі як Date.now() або Math.random(), форматування дат, залежне від локалі, зовнішні дані, які змінилися без знімка, недійсна вкладеність HTML та розширення браузера, які модифікують DOM. Див. помилку React 418.
Next.js надає подібний список у своєму офіційному посібнику з помилок гідратації, додаючи API, доступні лише в браузері, такі як window та localStorage, конфігурацію CSS-in-JS та HTML, модифікований шаром Edge/CDN.
Ілюстрація, згенерована ШІ: Звужте помилку до виразу, який може виробляти різні значення на сервері та в браузері, наприклад, дату, випадкове число, локаль або значення, отримане з браузера. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Практичним методом ізоляції є тимчасова заміна підозрілих динамічних секцій детермінованим текстом. Якщо помилка зникає, відновлюйте ці секції по одній. Зазвичай це швидше, ніж змінювати глобальні налаштування рендерингу, перш ніж ви дізнаєтеся, який компонент несе відповідальність.
Сигнал якості
Ви готові рухатися далі, коли можете назвати як компонент, що не працює, так і значення або структуру, що відрізняються. «Це відбувається десь у панелі приладів» — це все ще занадто широко. «Часова мітка в StatusCard виробляється незалежно на сервері та клієнті» — це діяльна інформація.
Крок 2: Видаліть недетерміновані значення з початкового рендерингу
Детермінований рендеринг означає, що ті самі вхідні дані виробляють той самий початковий інтерфейс. Значення, які змінюються незалежно між рендерингом на сервері та рендерингом у браузері, є частими джерелами невідповідностей.
Розглянемо цю проблемну патерну:
export default function LastUpdated() {
return <time>{new Date().toLocaleString()}</time>;
}
Сервер і браузер можуть виконувати цей код у різні моменти та в різних локалях або часових поясах. Краще рішення залежить від того, що сторінка повинна повідомляти.
Якщо часова мітка представляє дані сервера, обчисліть або отримайте її один раз на сервері та передайте те саме серіалізоване значення на клієнт:
Якщо значення дійсно залежить від браузера користувача, спочатку відрендеріть стабільний заповнювач і оновіть його після гідратації.
Ілюстрація, згенерована ШІ: Стабільне початкове значення може гідратуватися чисто, а потім специфічний для браузера вміст може бути застосований після монтування компонента. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Це працює, тому що сервер і перший клієнтський рендеринг виробляють той самий заповнювач. Документація useEffect в React описує цей двостадійний патерн для рідкісних випадків, коли вміст клієнта повинен відрізнятися від вмісту сервера.
Коли змінювати підхід
Якщо значення, доступне лише в браузері, є всією метою компонента — наприклад, редактор, відновлений з localStorage, або віджет, який не може змістовно рендеритися на сервері — примусове застосування патерну заповнювача та ефекту по всьому компоненту може додати непотрібну складність. У цьому випадку використовуйте навмисну межу «лише браузер» замість того, щоб удавати, що компонент можна рендерити на сервері.
Крок 3: Не читайте API, доступні лише в браузері, під час першого серверно-сумісного рендерингу
Поширене хибне уявлення в Next.js полягає в тому, що додавання 'use client' гарантує, що компонент рендериться лише в браузері. Це не так. Next.js пояснює, що Client Components є межею для стану, ефектів, обробників подій та API браузера, але Client Components все ще можуть брати участь у попередньому рендерингу. Див. поточну документацію use client.
На сервері localStorage не існує. Навіть розгалуження, таке як typeof window !== 'undefined', може виробляти різну розмітку під час першого рендерингу в браузері, що і React, і Next.js документують як причину невідповідності гідратації.
Для невеликих відмінностей перемістіть читання браузера в Effect. Для компонента, який дійсно повинен бути лише браузерним у Next.js, ви можете динамічно завантажити його з вимкненим SSR:
Ілюстрація, згенерована ШІ: Використовуйте межу «лише клієнт» для компонентів, які фундаментально залежать від API браузера, замість того, щоб дозволяти серверу та браузеру рендерити різні дерева. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Next.js документує ssr: false для Client Components у своєму посібнику з лінивого завантаження. Той самий посібник стверджує, що ssr: false не підтримується, коли ви намагаєтеся використовувати цю опцію безпосередньо в Server Component; перемістіть динамічний імпорт у Client Component.
React 19.3: першокласна опція «лише браузер»
React 19.3 представив API browser. Компонент може викликати use(browser()) всередині межі Suspense, щоб вивести цей компонент із серверного рендерингу. Сервер рендерить запасний варіант (fallback) Suspense, тоді як компонент рендериться нормально в браузері. Див. довідник API browser в React.
import { Suspense, use } from 'react';
import { browser } from 'react-dom';
function BrowserOnlyContent() {
use(browser('Requires browser APIs'));
return <ActualBrowserContent />;
}
export default function Example() {
return (
<Suspense fallback={<p>Loading...</p>}>
<BrowserOnlyContent />
</Suspense>
);
}
У застосунку React Server Components React стверджує, що use(browser()) повинен викликатися з Client Component. Також переконайтеся, що ваш фреймворк і встановлена версія React надають цей API, перш ніж впроваджувати його.
Крок 4: Зробіть дані сервера та перші дані клієнта тим самим знімком
Знімок — це точний стан даних, використаний для вироблення початкового HTML. Гідратація стає крихкою, якщо сервер рендерить одну версію даних, а клієнт негайно читає новішу або інакше впорядковану версію до завершення гідратації.
Ілюстрація, згенерована ШІ: Перший рендеринг клієнта повинен споживати той самий початковий знімок даних, який виробив HTML сервера; пізніші оновлення можуть відбуватися після гідратації. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Наприклад, припустімо, що сервер рендерить ціну $99, але клієнт негайно отримує той самий продукт і отримує $109 перед своїм першим рендерингом. Проблема не в тому, що дані змінилися; зміна даних є нормальною. Проблема в тому, що два середовища використовували різні початкові вхідні дані.
Сильна патерна така:
Отримайте початкові дані на сервері.
Відрендеріть HTML з цих даних.
Передайте або серіалізуйте ті самі початкові дані в компонент клієнта.
Після гідратації дозвольте клієнту ревалідувати та оновити дані, якщо існують новіші.
Правильна реалізація залежить від моделі отримання даних вашого фреймворку, але критерій якості залишається тим самим: HTML сервера та перше дерево клієнта повинні базуватися на тому самому логічному стані.
Коли змінювати підхід
Якщо вміст за своєю природою є реальним часом, і застарілий серверний знімок ввів би користувачів в оману — наприклад, віджет торгівлі в реальному часі або консоль операцій, що швидко змінюються — розгляньте можливість рендерингу стабільної оболонки на сервері та завантаження живої секції на клієнті. Це відмовляється від деякого серверного рендерингу для цього регіону, але може бути чеснішим, ніж гідратація проти даних, які гарантовано зміняться.
Крок 5: Виправте недійсний HTML, перш ніж звинувачувати React
Браузерам дозволено виправляти некоректний або недійсно вкладений HTML. Це виправлення може виробити структуру DOM, яка відрізняється від структури, яку очікує React, навіть якщо JSX виглядав візуально правдоподібно.
Ілюстрація, згенерована ШІ: Перевіряйте семантичну вкладеність HTML, коли дерево компонентів здається детермінованим, але браузер все одно конструює інший DOM. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Next.js явно перелічує приклади, такі як <div> всередині <p>, список всередині абзацу, вкладені якорі та вкладені кнопки, як причини проблем гідратації.
Наприклад, уникайте:
<p>
Intro text
<div>Details</div>
</p>
Замість цього використовуйте дійсну структуру:
<div>
<p>Intro text</p>
<div>Details</div>
</div>
Якщо бібліотека компонентів генерує розмітку, перевіряйте фінальний DOM, а не припускайте, що елементи-обгортки є дійсними. Правило лінтера або валідатор HTML можуть допомогти, але фактичний DOM браузера є тим, що React гідратує.
Крок 6: Виключіть код поза компонентом
Якщо ваша логіка рендерингу детермінована, а ваш HTML дійсний, перевірте, чи щось не модифікує HTML сервера перед тим, як React його гідратує.
Офіційна документація Next.js називає кілька можливостей:
Розширення браузера змінює сторінку перед завантаженням React.
Бібліотека CSS-in-JS налаштована неправильно для серверного рендерингу.
Функція Edge або CDN переписує або мінімізує HTML-відповідь.
На iOS автоматичне виявлення номерів телефонів, електронних адрес, дат або адрес може в деяких випадках перетворювати текст на посилання.
Використовуйте контрольовані порівняння. Тестуйте в приватному вікні браузера з вимкненими розширеннями. Якщо помилка виникає лише за CDN, порівняйте з відповіддю джерела. Якщо вона почалася після впровадження бібліотеки стилізації, дотримуйтесь офіційної конфігурації SSR цієї бібліотеки, а не застосовуйте загальний обхідний шлях для гідратації.
Сигнал якості
Ви ізолювали цей клас проблем, коли та сама збірка застосунку гідратується правильно в одному контрольованому середовищі, але не працює після того, як конкретне розширення браузера, проксі, трансформація CDN або інтеграція змінюють HTML.
Крок 7: Використовуйте suppressHydrationWarning лише для дійсно неминучої локальної різниці
React надає suppressHydrationWarning={true} для рідкісних випадків, коли текст або атрибути одного елемента не можуть розумно збігатися, наприклад, певні часові мітки.
Це не загальний механізм ремонту. Документація загальних пропів DOM в React стверджує, що опція працює лише на один рівень глибини і призначена як аварійний вихід. Посібник Next.js з гідратації також попереджає, що React не намагатиметься виправити невідповідний текстовий вміст, коли ця опція використовується.
Використовуйте її лише коли всі ці умови виконані:
Різниця є очікуваною та локалізованою.
Невідповідність не представляє неправильного стану застосунку.
Навколишня структура є стабільною.
Ви свідомо прийняли, що початкове значення сервера та браузера відрізняються.
Якщо додавання пропа змушує зникнути десятки попереджень, це привід для подальшого розслідування, а не ознака того, що базова проблема вирішена.
Крок 8: Перевірте виправлення в середовищах розробки та виробництва
Ілюстрація, згенерована ШІ: Після зміни коду перевірте чисте перезавантаження, правильну інтерактивність та виробничу збірку, замість того, щоб покладатися лише на накладання розробки. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Поведінка в середовищі розробки може відрізнятися від оптимізованої виробничої збірки. Після того, як помилка зникне локально, виконайте перевірку в стилі виробництва для вашого фреймворку. Для типового проєкту Next.js це часто означає збірку та запуск застосунку за допомогою ваших звичайних команд пакетного менеджера, а потім виконання нових навігацій та перезавантажень.
Використовуйте цей чек-лист перевірки:
Перевірка
Добра ознака
Якщо не вдається
Нове перезавантаження
Немає помилки гідратації в консолі
Перевірте найраніший компонент, що відрізняється
Початковий візуальний стан
Немає ненавмисного мерехтіння або заміни
Зробіть початковий стан детермінованим
Взаємодії
Кнопки, форми, меню та стан працюють нормально
Підтвердіть, що компонент все ще гідратується та обробники подій прикріплюються
Виробнича збірка
Той самий правильний результат, що й у розробці
Розслідкуйте поведінку даних, CDN, CSS або оптимізації, специфічну для виробництва
Розширення вимкнені
Результат не змінюється
Ідентифікуйте поведінку розширення, що модифікує DOM
Якщо ви безпосередньо володієте точкою входу React SSR замість використання фреймворку, hydrateRoot також підтримує зворотні виклики помилок, такі як onRecoverableError, які можуть допомогти у логуванні виробництва. Користувачі фреймворків загалом не повинні замінювати точку входу гідратації фреймворку лише для додавання власної обробки.
Коли спробувати іншу стратегію рендерингу
Ілюстрація, згенерована ШІ: Змінюйте стратегію, коли компонент фундаментально не може виробити змістовний HTML сервера, але тримайте межу «лише клієнт» якнайменшою, наскільки це практично. Це не реальний скріншот браузера, React або Next.js; використовуйте перевірені посилання на код і документацію в статті як джерело істини.
Іноді найкращим виправленням є не примусове впровадження компонента в SSR. Розгляньте іншу стратегію рендерингу, коли:
Компонент побудований навколо window, canvas, WebGL, вимірювань браузера або іншого API, доступного лише в браузері.
Сторонній віджет офіційно не підтримує SSR.
Змістовний вміст компонента повністю залежить від локального стану пристрою, такого як localStorage.
Дані в реальному часі змінюються так швидко, що зіставлення серверного знімка має мало цінності.
У цих випадках цільова межа «лише клієнт» може бути чистішою. Ключове слово — цільова. Вимкнення SSR для всієї сторінки, щоб розмістити один графік або редактор, може непотрібно пожертвувати корисним серверним рендерингом, поведінкою завантаження та іншими перевагами.
Поширені виправлення, які виглядають успішними, але не є такими
Ярлик
Чому це неповно
Кращий критерій
Додати 'use client' всюди
Client Components все ще можуть попередньо рендеритися в Next.js
Перемістіть логіку, доступну лише в браузері, після гідратації або ізолюйте її навмисно
Обгорнути логіку рендерингу в typeof window !== 'undefined'
Само розгалуження може створити різну розмітку першого рендерингу
Збережіть перший рендеринг ідентичним
Використовувати suppressHydrationWarning широко
Це приховує попередження, а не узгоджує стан застосунку
Використовуйте лише для очікуваної, локальної, неминучої невідповідності
Вимкнути SSR для всієї сторінки
Це може усунути симптом, видаливши гідратацію для занадто великої частини UI
Невідповідність може з'явитися лише при прямому запиті або жорсткому перезавантаженні
Тестуйте нові завантаження сторінок, відрендерених на сервері
Обмеження цих виправлень
Помилка гідратації повідомляє вам, що рендеринг сервера та клієнта розійшлися; вона не доводить чому. Той самий симптом може походити від логіки застосунку, мутації браузера, бібліотеки, CDN, некоректного HTML або змінних даних. Не існує єдиного фрагмента коду, який безпечно виправляє всі ці випадки.
Крім того, видалення попереджень гідратації не гарантує правильність в інших місцях. Компонент, що працює лише на клієнті, все ще може мати гонки даних. Детермінований перший рендеринг все ще може показувати застарілі дані після гідратації. Дійсний DOM все ще може містити проблеми доступності. Ставтеся до гідратації як до одного якісного шлюзу, а не єдиного.
Новий API browser в React 19.3 також не означає, що кожен фреймворк повинен негайно замінити свою встановлену патерну «лише браузер». Інтеграція фреймворку та встановлені версії мають значення. Якщо ваш проєкт використовує старішу версію React або Next.js, дотримуйтесь документації для цієї версії, а не копіюйте новіший API наосліп.
Надійний порядок прийняття рішень
Знайдіть найменший компонент, що не збігається.
Перевірте наявність змінних значень, таких як дати, випадкові числа, форматування локалі та дані, отримані двічі.
Видаліть API, доступні лише в браузері, з першого серверно-сумісного рендерингу.
Переконайтеся, що сервер і перший рендеринг клієнта використовують той самий знімок даних.
Перевірте структуру HTML.
Виключіть розширення, конфігурацію SSR CSS-in-JS та переписування CDN/Edge.
Використовуйте Effect, цільовий рендеринг лише для клієнта або use(browser()) в React 19.3 лише коли вміст дійсно залежить від браузера.
Збережіть suppressHydrationWarning для малих, навмисних невідповідностей.
Перевірте за допомогою нового перезавантаження та виробничої збірки.
Тривале виправлення — це не «змусити React перестати скаржитися». Це зробити контракт початкового рендерингу явним: сервер і браузер повинні погоджуватися щодо першого UI, або секція, доступна лише в браузері, повинна бути навмисно ізольована, щоб React не просили гідратувати розмітку, яка ніколи не могла б збігтися.