HydrationNext.jsReactHydration8 mnt baca
Memperbaiki Text Content Did Not Match Server di Next.js
Error hydration klasik: HTML dari server berbeda dengan hasil render client, penyebab umum dan polanya.
1 · Gejala & Log Pesan Error
error.log
Error: Text content does not match server-rendered HTML.
Warning: Text content did not match. Server: "Selamat pagi"
Client: "Selamat malam"
at throwOnHydrationMismatch (react-dom.development.js)
at tryToClaimNextHydratableInstance ...
This error happened while generating the page. Any console logs
will be displayed in the terminal window.2 · Akar Penyebab Masalah
Server dan Client Me-render Hal yang Berbeda
Hydration = React "menempelkan" interaktivitas ke HTML yang sudah di-render server. Ia mengasumsikan output render pertama client identik dengan HTML server. Kalau berbeda, React protes.
Penyebab Paling Umum
- Nilai acak / waktu saat render:
new Date(),Math.random(), atauDate.now()menghasilkan nilai berbeda di server vs client. - Browser-only API saat render:
window.innerWidth,localStorage. Tidak ada di server, menghasilkanundefinedlalu berubah di client. - Ekstensi browser yang menyuntik DOM (ad-blocker, translator). Di luar kontrol kode, tapi sering jadi tersangka.
3 · Solusi & Diff Kode
✕ Sebelum (bermasalah)
before
// ❌ Berbeda tiap render → hydration mismatch
export default function Greeting() {
const hour = new Date().getHours(); // server: 08, client: 20?
return <p>{hour < 12 ? "Selamat pagi" : "Selamat malam"}</p>;
}✓ Sesudah (diperbaiki)
after
// ✔ Tunda ke client lewat useEffect + state
"use client";
import { useEffect, useState } from "react";
export default function Greeting() {
const [hour, setHour] = useState<number | null>(null);
useEffect(() => {
setHour(new Date().getHours()); // hanya jalan di client
}, []);
if (hour === null) return <p>Memuat...</p>; // sama di server & client
return <p>{hour < 12 ? "Selamat pagi" : "Selamat malam"}</p>;
}Langkah Perbaikan
- Identifikasi sumber perbedaan: cari
Date,Math.random(), atau akseswindow/localStoragelangsung di body render. - Pindahkan ke
useEffect: render pertama harus deterministik (sama di server & client), nilai dinamis diisi setelah mount. - Alternatif: bungkus komponen browser-only dengan
dynamic(() => import('./Widget'), { ssr: false }). Komponen tidak di-render server sama sekali. - Untuk teks tanggal/waktu: format di server dengan timezone eksplisit, atau tampilkan skeleton saat
hour === nullseperti contoh di atas.