Skip to main content
BlogWeb Dev

Error Hydration Next.js: Penyebab dan Cara Memperbaikinya

Apa arti sebenarnya error hydration mismatch di Next.js, sekumpulan kecil penyebabnya, dan cara saya memperbaiki masing-masing dengan useEffect, dynamic import, dan suppressHydrationWarning.

Matthews Wong

Software Engineer

Agustus 20269 menit baca
Error Hydration Next.js: Penyebab dan Cara Memperbaikinya

Foto oleh Bernd Dittrich on Unsplash

Ringkasan Utama

Error hydration Next.js berarti HTML yang dirender di server tidak cocok dengan hasil render pertama React di sisi klien. Perbaikannya adalah membuat kedua render identik, atau menunda nilai yang benar-benar khusus klien ke dalam useEffect agar output awal tetap sama di kedua sisi.

Sedikit error Next.js yang membingungkan pada pandangan pertama seperti hydration failed because the initial UI does not match what was rendered on the server. Halaman sering terlihat baik-baik saja, tetapi konsol menyala merah dan interaktivitas terasa rusak. Saya sudah sering mengalaminya di berbagai proyek App Router, dan penyebabnya selalu berbentuk masalah yang sama.

Di tulisan ini saya jelaskan apa itu hydration sebenarnya, kenapa mismatch terjadi, cara yang bisa diulang untuk melacak baris penyebabnya, dan perbaikan yang benar untuk tiap penyebab. Tanpa menebak, hanya pola yang benar-benar direkomendasikan oleh dokumentasi React dan Next.js.

Apa arti sebenarnya error hydration

Next.js melakukan prerender halaman menjadi HTML di server. Di browser, React lalu melakukan hydrate terhadap HTML itu, menelusuri pohon komponen yang sama dan memasang event handler pada DOM yang sudah ada alih-alih membuatnya ulang. Hydration hanya berhasil jika pohon yang dibangun React di klien menghasilkan output yang sama seperti di server.

Ketika render klien pertama berbeda dari HTML server, React tidak bisa mempercayai markup tersebut. Ia memberi peringatan di mode development dan jatuh kembali ke render klien untuk subtree itu, yang lebih lambat dan bisa memasang handler ke elemen yang salah. Karena itu dokumentasinya tegas: perlakukan mismatch sebagai bug dan perbaiki, jangan menutupinya.

Penyebab yang umum

Hampir setiap hydration mismatch yang saya debug bermuara pada satu dari sekumpulan kecil penyebab. Dokumentasi React dan Next.js menyebut pelaku yang sama, jadi begitu Anda mengenali bentuknya, biasanya Anda bisa menamai penyebabnya sebelum membuka komponennya.

  • Penyusunan HTML yang tidak valid dan diam-diam diperbaiki browser, seperti div di dalam p, atau p di dalam p lain. Server memancarkan markup yang Anda maksud, browser menulisnya ulang, dan kedua pohon tidak lagi sejajar.
  • API khusus browser yang dipakai saat render, seperti window, localStorage, atau matchMedia. Semua ini tidak ada di server, sehingga cabang server dan klien berbeda, terutama saat dijaga dengan pemeriksaan typeof window.
  • Output yang bergantung waktu dan locale saat render, seperti konstruktor Date, toLocaleTimeString, atau format angka dan mata uang yang berbeda antara timezone atau locale server dan pengunjung.
  • Nilai acak atau non-deterministik yang dibuat saat render, seperti Math.random atau id unik baru, yang secara alami menghasilkan hasil berbeda setiap kali sehingga berbeda antara server dan klien.
  • Hal-hal di luar kode Anda sepenuhnya, yang paling umum adalah ekstensi browser yang menyuntik atau menulis ulang atribut dan markup sebelum React melakukan hydrate, ditambah minifikasi CDN atau setup CSS-in-JS yang salah konfigurasi.

Cara sistematis untuk menemukannya

Teks error saja jarang menunjuk baris yang persis, jadi saya menelusuri checklist yang sama setiap kali alih-alih menebak:

  1. Baca diff yang dicetak React. Next.js modern menampilkan teks atau atribut yang mismatch; catat apakah itu konten teks, sebuah atribut, atau seluruh elemen yang muncul hanya di satu sisi.
  2. Reproduksi secara bersih. Buka halaman di jendela private dengan ekstensi dimatikan. Jika error hilang, sebuah ekstensi sedang menulis ulang HTML Anda dan kode Anda baik-baik saja.
  3. Bisect pohonnya. Komentari atau hubung-singkatkan separuh komponen sampai peringatan berhenti, yang mengisolasi subtree penyebab dengan cepat tanpa membaca setiap baris.
  4. Tanyakan apa yang bisa berbeda antara server dan klien untuk node itu: jam, locale, nilai acak, pemeriksaan window, atau tag bersarang yang akan diperbaiki browser. Jawaban itu menamai perbaikannya.

Perbaikan yang benar: jaga kedua render tetap identik

Perbaikan utama bukanlah membungkam peringatan, melainkan membuat render server dan render klien pertama menghasilkan output yang sama. Untuk nilai yang benar-benar khusus klien, render placeholder yang stabil di server dan isi nilai sebenarnya setelah mount di dalam useEffect, yang hanya berjalan di browser.

// BEFORE — hydration mismatch.
// The server renders its own clock time; the browser renders a
// different time at hydration, so the initial UIs never match.
export default function LastUpdated() {
  return <p>Last updated: {new Date().toLocaleTimeString()}</p>;
}

// AFTER — stable on the server, filled in on the client.
// The server and first client render both output an empty string
// (they match), then useEffect swaps in the real time after mount.
"use client";
import { useState, useEffect } from "react";

export default function LastUpdated() {
  const [time, setTime] = useState("");

  useEffect(() => {
    setTime(new Date().toLocaleTimeString());
  }, []);

  return <p>Last updated: {time}</p>;
}

Ini adalah pola yang direkomendasikan dokumentasi Next.js untuk nilai yang bergantung waktu dan khusus browser. Server dan render klien pertama sama-sama memancarkan placeholder kosong yang sama, jadi hydration berhasil, lalu effect memperbarui DOM sesaat kemudian tanpa mismatch apa pun.

Gunakan next/dynamic dengan ssr false ketika sebuah komponen memang tidak pernah bisa dirender secara bermakna di server, seperti chart yang bergantung pada ukuran window. Ini menonaktifkan prerender untuk komponen itu saja, jadi tidak ada yang dipancarkan di server untuk di-mismatch.

Kapan suppressHydrationWarning adalah alat yang tepat

Kadang sebuah perbedaan memang tidak terhindarkan untuk satu elemen, contoh klasiknya adalah timestamp. Untuk kasus sempit itu React menyediakan suppressHydrationWarning. Jika sebuah komponen sama sekali tidak boleh dirender di server, menonaktifkan SSR dengan dynamic import adalah pilihan yang lebih bersih.

// Escape hatch: only for a single element whose value is
// genuinely unavoidable to match, such as a timestamp.
// It patches one level deep only — do not wrap whole subtrees.
<time dateTime="2026-08-01" suppressHydrationWarning>
  {new Date().toLocaleDateString()}
</time>

// For content that must not render on the server at all,
// disable prerendering for that component instead:
import dynamic from "next/dynamic";

const ClientOnlyChart = dynamic(() => import("./chart"), {
  ssr: false,
});

Perlakukan suppressHydrationWarning sebagai escape hatch, bukan default. Ia hanya bekerja satu level dalam, dan saat disetel React tidak akan mencoba menambal konten teks yang mismatch, jadi apa pun di bawah elemen itu berjuang sendiri.

Jangan membungkus seluruh subtree dengan suppressHydrationWarning hanya agar konsol merah hilang. Ia menyembunyikan bug nyata satu level di bawahnya, dan karena React berhenti menambal teks, Anda bisa mengirim UI yang diam-diam berbeda antara server dan klien tanpa peringatan apa pun.

Penyebab dan perbaikan sekilas

Berikut pemetaan yang selalu saya ingat, mencocokkan tiap penyebab umum dengan perbaikan yang didukung dokumentasi:

PenyebabKenapa mismatchPerbaikan yang benar
Penyusunan HTML tidak validBrowser memperbaiki markup, sehingga DOM tidak lagi cocok dengan pohon serverPerbaiki penyusunan agar elemen blok tidak diletakkan di dalam p atau a di dalam a
API khusus browser saat renderwindow atau localStorage undefined di server, sehingga cabangnya berbedaPindahkan akses ke dalam useEffect dan render placeholder stabil lebih dulu
Format Date, locale, atau angkaTimezone atau locale server berbeda dari pengunjung, sehingga teksnya berbedaFormat di dalam useEffect, atau pakai suppressHydrationWarning pada satu timestamp
Nilai acak atau unikMath.random dan id baru menghasilkan nilai berbeda tiap renderBuat nilainya di useEffect, atau pakai id stabil dari useId
Ekstensi browser atau CDNKode pihak ketiga menulis ulang HTML sebelum React melakukan hydratePastikan di profil bersih; matikan auto-minify CDN; itu bukan kode Anda

Penutup

Error hydration terasa misterius sampai Anda menghayati satu aturan di baliknya: render server dan render klien pertama harus menghasilkan output yang identik. Begitu itu klik, setiap perbaikan mengikuti dari menanyakan apa yang bisa berbeda untuk sebuah node, lalu memakai useEffect, dynamic import, atau suppressHydrationWarning yang tepat sasaran dalam urutan itu.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa arti hydration failed because the initial UI does not match?

Artinya HTML yang dirender Next.js di server tidak cocok dengan hasil render pertama React di browser. React melakukan hydrate dengan memasang handler pada HTML server yang ada, jadi jika pohon klien berbeda, ia tidak bisa mempercayai markup itu, memberi peringatan di development, dan jatuh ke render klien yang lebih lambat untuk subtree tersebut.

Apa penyebab paling umum error hydration di Next.js?

Penyebab umumnya adalah penyusunan HTML tidak valid yang diperbaiki browser, API khusus browser seperti window atau localStorage saat render, output yang bergantung waktu atau locale seperti konstruktor Date dan format angka, nilai acak seperti Math.random, serta gangguan pihak ketiga dari ekstensi browser atau minifikasi CDN.

Bagaimana memperbaiki mismatch yang disebabkan tanggal atau timestamp?

Render placeholder stabil di server dan setel nilai sebenarnya di dalam useEffect, yang hanya berjalan di browser, sehingga render server dan render klien pertama cocok. Untuk satu elemen timestamp yang tidak terhindarkan, Anda bisa memakai suppressHydrationWarning, tetapi gunakan seperlunya sebagai escape hatch.

Kapan sebaiknya memakai suppressHydrationWarning?

Hanya ketika teks atau atribut satu elemen memang tidak terhindarkan untuk dicocokkan, seperti timestamp. Ia bekerja satu level dalam dan menghentikan React menambal teks yang mismatch, jadi jangan pernah membungkus seluruh subtree dengannya. Untuk komponen yang tidak bisa dirender di server, nonaktifkan SSR dengan next/dynamic dan ssr false.

Apakah ekstensi browser bisa menyebabkan error hydration?

Ya. Ekstensi bisa menyuntik atau menulis ulang atribut dan markup sebelum React melakukan hydrate, menghasilkan mismatch yang bukan dari kode Anda. Reproduksi halaman di jendela private dengan ekstensi dimatikan; jika error hilang, ekstensi adalah penyebabnya dan komponen Anda baik-baik saja.