Hydration failed в Next.js: почему возникает ошибка и как её исправить
Ошибка Hydration failed because the initial UI does not match - одна из самых частых проблем в Next.js.
Обычно она появляется, когда HTML, сгенерированный на сервере, отличается от того, что React пытается отрендерить в браузере.
Проще говоря:
сервер отдал один HTML,
клиент сгенерировал другой,
React обнаружил несовпадение,
hydration ломается.
Как выглядит ошибка
Hydration failed because the initial UI does not match what was rendered on the server
Иногда дополнительно появляются:
Text content does not match
Expected server HTML to contain
Hydration mismatch
Почему возникает hydration error
1. Использование window или document
Это самая частая причина.
Неправильно:
export default function Page() {
const width = window.innerWidth
return <div>{width}</div>
}На сервере window не существует.
Правильно:
"use client"
import { useEffect, useState } from "react"
export default function Page() {
const [width, setWidth] = useState(0)
useEffect(() => {
setWidth(window.innerWidth)
}, [])
return <div>{width}</div>
}2. Разный контент на сервере и клиенте
Например:
<div>{Date.now()}</div>Сервер и браузер генерируют разное время.
Решение:
Перенеси данные в useEffect.
"use client"
import { useEffect, useState } from "react"
export default function Time() {
const [time, setTime] = useState("")
useEffect(() => {
setTime(Date.now().toString())
}, [])
return <div>{time}</div>
}3. Использование random значений
Ошибка:
<div>{Math.random()}</div>На сервере и клиенте значения разные.
4. Условный рендеринг через browser API
Проблема:
const isMobile = navigator.userAgent.includes("Mobile")Лучше:
"use client"
import { useEffect, useState } from "react"
export default function MobileCheck() {
const [isMobile, setIsMobile] = useState(false)
useEffect(() => {
setIsMobile(window.innerWidth < 768)
}, [])
return <div>{isMobile ? "Mobile" : "Desktop"}</div>
}5. Компонент должен быть Client Component
В App Router Next.js серверные компоненты - default behavior.
Если используешь:
stateuseEffectbrowser API
нужен:
"use client"
6. Ошибки сторонних библиотек
Некоторые библиотеки:
charts
sliders
animation libs
не поддерживают SSR.
Решение через dynamic import
import dynamic from "next/dynamic"
const Chart = dynamic(() => import("./Chart"), {
ssr: false,
})Как быстро понять причину hydration error
Проверь:
есть ли window
используется ли Date.now()
есть ли Math.random()
есть ли browser API
совпадает ли HTML сервера и клиента
Лучший способ избежать hydration problems
Правило:
Всё, что зависит от браузера:
рендерить только на клиенте.
Полезный паттерн
"use client"
import { useEffect, useState } from "react"
export default function Component() {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) return null
return <div>Client Content</div>
}Этот подход часто спасает сложные UI-компоненты.
Итог
Hydration failed в Next.js возникает, когда сервер и клиент рендерят разный HTML.
Самые частые причины:
window
document
Date.now()
Math.random()
browser API
SSR-несовместимые библиотеки
Главное правило:
сервер и клиент должны генерировать одинаковую разметку до hydration.
Комментарии
Чтобы оставить комментарий, войдите в аккаунт.