Cara Mengatasi Error “Hydration failed because the initial UI does not match” di Next.js [Panduan Terbaru 2026]
Jika Anda sedang mengembangkan aplikasi dengan Next.js dan tiba-tiba muncul error “Hydration failed because the initial UI does not match what was rendered on the server”, Anda tidak sendirian. Error ini sangat sering terjadi di lingkungan React 19 dan Next.js 15 dengan App Router, dengan ribuan komentar di GitHub Discussions dan selalu menjadi trending di Stack Overflow. Artikel ini menyediakan panduan lengkap yang diperbarui untuk tahun 2026 untuk mengidentifikasi penyebab dan menerapkan solusi spesifik, dilengkapi dengan contoh kode nyata.
- Apa Error Ini? Gejala yang Akan Anda Alami
- Penyebab Error Ini
- Penyebab 1: Menggunakan API Khusus Browser (window / localStorage / document)
- Penyebab 2: Nilai Non-deterministik Seperti Timestamp dan Angka Acak
- Penyebab 3: Struktur Nesting HTML yang Tidak Valid
- Penyebab 4: Ekstensi Browser yang Memodifikasi DOM
- Penyebab 5: Script Pihak Ketiga dan Ketidakkonsistenan Konten CMS
- Solusi 1: Isolasi Logika Khusus Client dengan Hook useEffect (Direkomendasikan)
- Solusi 2: Nonaktifkan SSR dengan next/dynamic
- Solusi 3: Perbaiki Nesting HTML dan Penggunaan suppressHydrationWarning yang Tepat
- Cara Mencegah Error Ini
- Ringkasan
- Referensi
Apa Error Ini? Gejala yang Akan Anda Alami
Hydration Error di Next.js terjadi ketika HTML yang di-render di sisi server tidak cocok dengan HTML yang coba di-render oleh React di sisi client saat render awal.
Secara spesifik, pesan error berikut akan muncul di console pengembang browser Anda:
Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Anda mungkin juga melihat variasi berikut:
Error: Text content does not match server-rendered HTML.
Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
Ketika error ini terjadi, Next.js membuang hasil server-side rendering (SSR) dan me-render ulang seluruh halaman di sisi client. Hal ini menyebabkan beberapa konsekuensi serius:
- Penurunan performa signifikan: Semua manfaat SSR hilang, membuat pemuatan halaman awal lebih lambat
- Dampak negatif SEO: Crawler mesin pencari mungkin tidak menerima HTML yang benar
- Pengalaman pengguna buruk: Terjadi kedipan layar (flash of content)
- Pengalaman developer memburuk: Console dipenuhi error, memudahkan melewatkan masalah lain
Di Next.js 15 yang dikombinasikan dengan React 19, pemeriksaan konsistensi hydration menjadi lebih ketat, dan kasus yang sebelumnya hanya berupa peringatan kini diperlakukan sebagai error.
Penyebab Error Ini
Penyebab 1: Menggunakan API Khusus Browser (window / localStorage / document)
Penyebab paling sering adalah mereferensikan langsung API khusus browser seperti window, localStorage, dan document selama proses rendering. Objek-objek ini tidak ada di sisi server.
Di server, objek-objek ini bernilai undefined. Oleh karena itu, menggunakan cabang kondisional seperti typeof window !== 'undefined' dalam logika rendering menyebabkan output yang berbeda antara server dan client, memicu hydration error.
// SALAH: Ini menyebabkan hydration error
function MyComponent() {
const isClient = typeof window !== 'undefined';
return <div>{isClient ? 'Client' : 'Server'}</div>;
}
Penyebab 2: Nilai Non-deterministik Seperti Timestamp dan Angka Acak
Menggunakan fungsi yang mengembalikan nilai berbeda setiap kali dipanggil, seperti new Date() atau Math.random(), selama rendering menyebabkan ketidakcocokan antara output server dan client.
// SALAH: Timestamp berbeda antara server dan client
function Clock() {
return <p>Waktu saat ini: {new Date().toLocaleTimeString()}</p>;
}
Selalu ada jeda waktu antara saat server merender dan saat client melakukan hydrate, sehingga waktu yang ditampilkan secara alami akan berbeda, menghasilkan error ketidakcocokan konten teks.
Penyebab 3: Struktur Nesting HTML yang Tidak Valid
Struktur nesting yang melanggar spesifikasi HTML (misalnya, menempatkan <div> di dalam tag <p>) menyebabkan parser HTML browser secara otomatis mengoreksi DOM, membuat struktur DOM yang berbeda dari HTML yang di-render di server.
// SALAH: <div> tidak bisa ditempatkan di dalam <p>
function BadNesting() {
return (
<p>
Teks
<div>Ini adalah nesting yang tidak valid</div>
</p>
);
}
Browser secara otomatis mengoreksi HTML yang tidak valid ini, menyebabkan ketidakcocokan antara HTML yang dikirim server dan DOM browser.
Penyebab 4: Ekstensi Browser yang Memodifikasi DOM
Ekstensi browser seperti Colorzilla dan Grammarly dapat menyuntikkan atribut atau elemen HTML ke dalam DOM halaman. Misalnya, Colorzilla menambahkan atribut cz-shortcut-listen="true" ke tag <body>. Ini dilaporkan sebagai masalah yang sangat menonjol di lingkungan Next.js 15 + React 19.
Penyebab 5: Script Pihak Ketiga dan Ketidakkonsistenan Konten CMS
Script eksternal (tag iklan, analytics, dll.) yang memodifikasi DOM, atau konten HTML yang diperoleh dari CMS yang mengandung struktur nesting tidak valid, dapat memicu hydration error.
Solusi 1: Isolasi Logika Khusus Client dengan Hook useEffect (Direkomendasikan)
Solusi yang paling efektif dan direkomendasikan adalah memindahkan logika yang bergantung pada browser ke dalam hook useEffect dan hanya menghasilkan nilai deterministik selama render awal.
Langkah 1: Implementasikan Pola Manajemen State Client
Pertama, definisikan State dengan nilai awal yang sama untuk server dan client, kemudian perbarui ke nilai sisi client dengan useEffect.
'use client';
import { useState, useEffect } from 'react';
function ThemeProvider({ children }) {
// Gunakan nilai yang aman untuk server sebagai state awal
const [theme, setTheme] = useState('light');
const [mounted, setMounted] = useState(false);
useEffect(() => {
// Hanya akses localStorage di client
const savedTheme = localStorage.getItem('theme') || 'light';
setTheme(savedTheme);
setMounted(true);
}, []);
// Sebelum mount, kembalikan output yang sama dengan server
if (!mounted) {
return <div data-theme="light">{children}</div>;
}
return <div data-theme={theme}>{children}</div>;
}
Langkah 2: Atur Timestamp dan Nilai Acak di dalam useEffect
'use client';
import { useState, useEffect } from 'react';
function Clock() {
const [currentTime, setCurrentTime] = useState('');
useEffect(() => {
const updateTime = () => {
setCurrentTime(new Date().toLocaleTimeString());
};
updateTime();
const timer = setInterval(updateTime, 1000);
return () => clearInterval(timer);
}, []);
// Tampilkan string kosong pada render awal (cocok dengan server)
return <p>Waktu saat ini: {currentTime || 'Memuat...'}</p>;
}
Langkah 3: Buat Dapat Digunakan Ulang dengan Custom Hook
Jika Anda menggunakan pola ini di beberapa komponen, ekstrak ke custom hook.
'use client';
import { useState, useEffect } from 'react';
// Custom hook untuk mengelola status mount
function useHasMounted() {
const [hasMounted, setHasMounted] = useState(false);
useEffect(() => {
setHasMounted(true);
}, []);
return hasMounted;
}
// Contoh penggunaan
function ClientOnlyComponent() {
const hasMounted = useHasMounted();
if (!hasMounted) {
return <div>Memuat...</div>;
}
return <div>Lebar jendela: {window.innerWidth}px</div>;
}
Catatan Penting
useEffecttidak dieksekusi di sisi server, sehingga API browser dapat digunakan dengan aman di dalamnya- Tampilkan placeholder atau UI fallback selama render awal untuk menjaga pengalaman pengguna
- Pola ini mungkin sebentar menampilkan placeholder, tetapi jauh lebih ringan daripada re-rendering seluruh halaman yang disebabkan oleh hydration error
Solusi 2: Nonaktifkan SSR dengan next/dynamic
Untuk komponen yang sulit ditangani dengan pola useEffect (misalnya, library pihak ketiga yang sangat bergantung pada API browser), menonaktifkan SSR sepenuhnya menggunakan next/dynamic efektif.
import dynamic from 'next/dynamic';
// Import dengan SSR dinonaktifkan
const MapComponent = dynamic(
() => import('../components/Map'),
{
ssr: false,
loading: () => <div className="map-placeholder">Memuat peta...</div>
}
);
// Contoh penggunaan
function LocationPage() {
return (
<div>
<h1>Lokasi Toko</h1>
<MapComponent />
</div>
);
}
Metode ini sangat efektif dalam kasus berikut:
- Library peta (Leaflet, Google Maps, dll.): Karena bergantung pada objek
window - Editor teks kaya (Quill, TipTap, dll.): Karena langsung memanipulasi objek
document - Library grafik (Chart.js, D3.js, dll.): Karena melakukan operasi DOM SVG atau Canvas
Namun, komponen dengan SSR dinonaktifkan tidak termasuk dalam HTML awal, jadi hindari ini untuk konten yang penting untuk SEO. Praktik terbaik adalah membatasi penggunaannya pada elemen interaktif selain konten utama (peta, grafik, editor, dll.).
Solusi 3: Perbaiki Nesting HTML dan Penggunaan suppressHydrationWarning yang Tepat
Sebagai solusi tingkat lanjut, Anda dapat memperbaiki struktur nesting HTML dan menggunakan suppressHydrationWarning untuk kasus yang tidak dapat dihindari.
Memperbaiki Nesting HTML yang Tidak Valid
Pertama, periksa apakah ada nesting HTML yang tidak valid. Berikut adalah pola tidak valid yang umum dan koreksinya:
// SALAH: <div> di dalam <p>
<p>Teks<div>Elemen blok</div></p>
// BENAR: Ubah ke <div>
<div>Teks<div>Elemen blok</div></div>
// SALAH: <a> di dalam <a>
<a href="/induk">
Link induk
<a href="/anak">Link anak</a>
</a>
// BENAR: Hapus nesting
<div>
<a href="/induk">Link induk</a>
<a href="/anak">Link anak</a>
</div>
Penggunaan Terbatas suppressHydrationWarning
Untuk elemen kecil di mana perbedaan nilai server-client tidak dapat dihindari (seperti tampilan timestamp), Anda dapat menggunakan suppressHydrationWarning.
// Gunakan hanya ketika perbedaan dapat diterima, seperti timestamp
<time suppressHydrationWarning>
{new Date().toLocaleDateString()}
</time>
Catatan penting: suppressHydrationWarning hanya menyembunyikan error; tidak menyelesaikan masalah yang mendasarinya. Ini hanya berlaku untuk satu kedalaman text node, dan ketidakcocokan elemen anak tetap terdeteksi. Gunakan dengan hemat dan hanya ketika benar-benar diperlukan.
Menangani Ekstensi Browser
Jika ekstensi browser adalah penyebabnya, Anda dapat menambahkan suppressHydrationWarning ke tag <body>.
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html lang="id">
<body suppressHydrationWarning>{children}</body>
</html>
);
}
Ini menekan peringatan yang disebabkan oleh atribut yang ditambahkan ekstensi ke body. Namun, ketidakcocokan pada anak langsung body tetap terdeteksi.
Cara Mencegah Error Ini
Untuk mencegah hydration error secara proaktif, terapkan langkah-langkah pencegahan ini dalam alur kerja pengembangan harian Anda.
1. Jaga Render Awal Tetap “Deterministik”
Prinsip terpenting adalah menjamin output yang identik untuk render awal server dan client. Nilai yang bergantung pada lingkungan (API browser, timestamp, angka acak, dll.) harus selalu ditempatkan di dalam useEffect.
2. Pisahkan dengan Jelas Server Components dan Client Components
Di App Router Next.js 15, komponen adalah Server Components secara default. Saat menggunakan API browser atau React hooks (useState, useEffect, dll.), selalu tambahkan direktif 'use client' di bagian atas file.
3. Manfaatkan Aturan ESLint
eslint-plugin-react mencakup aturan untuk mendeteksi nesting HTML yang tidak valid (seperti jsx-no-invalid-html-nesting). Integrasikan ke dalam pipeline CI Anda untuk pemeriksaan otomatis.
4. Pengujian Berkala
Uji secara berkala dalam mode incognito (private browsing) atau dengan ekstensi browser dinonaktifkan untuk mengisolasi error terkait ekstensi.
5. Desain Responsif Berbasis CSS
Alih-alih membagi markup berdasarkan ukuran viewport, gunakan CSS media queries atau display: none untuk mengalihkan visibilitas, menjaga struktur HTML konsisten antara server dan client.
// SALAH: Pembagian viewport di JS
{isMobile ? <MobileNav /> : <DesktopNav />}
// BENAR: Pergantian berbasis CSS
<MobileNav className="block md:hidden" />
<DesktopNav className="hidden md:block" />
Ringkasan
Hydration Error Next.js “Hydration failed because the initial UI does not match what was rendered on the server” terjadi karena ketidakcocokan antara SSR/SSG dan rendering sisi client, menjadikannya salah satu error yang paling sering ditemui dalam pengembangan Next.js.
Poin-poin Utama:
- Inti masalah adalah “hasil render awal yang berbeda antara server dan client”
- Penyebab paling umum: penggunaan langsung API khusus browser, rendering nilai non-deterministik, nesting HTML tidak valid
- Solusi yang direkomendasikan: rendering bertahap dengan
useEffect, nonaktifkan SSR dengannext/dynamicjika diperlukan suppressHydrationWarningharus digunakan sebagai pilihan terakhir, dengan cakupan terbatas- Pencegahan: jaga render awal tetap deterministik, pisahkan dengan jelas Server/Client Components
Jika solusi-solusi ini tidak menyelesaikan masalah, periksa hal berikut:
- Perbarui Next.js dan React ke versi terbaru
- Hapus folder
node_modulesdan.nextlalu build ulang - Cari kasus serupa di GitHub Discussions Next.js (https://github.com/vercel/next.js/discussions)
- Buat reproduksi minimal dan laporkan sebagai Issue
Referensi
- Dokumentasi Resmi Next.js: React Hydration Error
- GitHub Discussion: Hydration failed because the initial UI does not match (3000+ komentar)
- Next.js Hydration Errors in 2026: The Real Causes, Fixes, and Prevention Checklist – Medium
- How to Fix Hydration Errors in server-rendered Components in Next.js – GeeksforGeeks
- Fixing Hydration Errors in server-rendered Components – Sentry
- Resolving hydration mismatch errors in Next.js – LogRocket Blog
- GitHub Discussion: Hydration Error in Next.js 15 with React 19

コメント