Cara mengakses window aman di Next.js App Router adalah membacanya hanya di Client Component dan hanya di dalam useEffect atau event handler. Server tidak punya objek window, jadi kode yang membacanya saat render memicu error window is not defined.
Kenapa Window Error di Next Js App Router
Server Component di-render di server lebih dulu sebelum dikirim ke browser. Prerender ini berjalan di Node.js tanpa DOM.
Baris seperti const lebar = window.innerWidth di body komponen langsung dieksekusi server. Server tidak mengenal window, proses berhenti dan build atau navigasi gagal.
- Guard
typeof window !== "undefined"berguna untuk kode campuran yang kadang jalan di server. - Guard tidak cukup kalau nilai dari
windowmembuat HTML server berbeda dengan HTML awal client. - Kode murni browser seperti ukuran layar, scroll, dan
localStoragelebih aman dipindah ke efek atau handler.
Pola Aman Mengakses Window di Client Component
Render awal memakai nilai default, lalu browser memperbarui state setelah mount. Ini mencegah hydration mismatch.
- Tambahkan
"use client"di baris pertama file komponen. - Buat state awal dengan nilai netral, misalnya
0ataunull. - Baca
windowdi dalamuseEffect, simpan ke state. - Pasang dan bersihkan
resize listenerbila nilai harus responsif.
"use client";
import { useEffect, useState } from "react";
export default function LebarLayar() {
const [lebar, setLebar] = useState(0);
useEffect(() => {
const update = () => setLebar(window.innerWidth);
update();
window.addEventListener("resize", update);
return () => window.removeEventListener("resize", update);
}, []);
return <p>Lebar layar: {lebar}px</p>;
}
- Jangan baca
windowsaat render atau sebagai initial valueuseState. - Sinkronkan nilai browser lewat state agar server dan client memulai dari HTML yang sama.
- Tangani perubahan ukuran dengan listener dan hapus listener saat unmount.
Tabel Pilihan Teknik Akses Window
| Teknik | Cocok Untuk | Catatan |
|---|---|---|
| useEffect | Ukuran layar, scroll, media query | Render awal sama, update setelah mount |
| Event handler | Klik, submit, resize manual | Berjalan di browser, tanpa guard tambahan |
| dynamic ssr false | Komponen browser-only | Matikan prerender untuk file tersebut |
useEffect cocok untuk membaca nilai setelah halaman tampil. Event handler cocok untuk aksi pengguna. Dynamic import cocok ketika seluruh komponen bergantung pada API browser atau library peta dan chart.
import dynamic from "next/dynamic";
const PetaBrowser = dynamic(() => import("./PetaBrowser"), {
ssr: false,
loading: () => <p>Memuat peta...</p>,
});
export default function Halaman() {
return <PetaBrowser />;
}
FAQ
Apakah typeof window cukup di Server Component?
Tidak, Hook dan browser API tetap tidak boleh dipakai di Server Component meski memakai guard tersebut.
Bagaimana membaca ukuran layar tanpa hydration error?
Render nilai default dulu di Client Component, lalu perbarui lewat useEffect setelah mount.
Kapan memakai dynamic dengan ssr false?
Pakai saat seluruh komponen hanya bisa jalan di browser, misalnya peta interaktif atau editor visual.