Your code works in npm run dev sometimes, then crashes the build with ReferenceError: window is not defined. This is the single most common SSR gotcha in Next.js. Here is exactly why it happens and how to fix it.
The Error
ReferenceError: window is not defined (also seen as: document is not defined, localStorage is not defined, navigator is not defined)
Why This Happens
Next.js runs your components on the server first (in Node.js) to generate HTML. Node has no window, document, or localStorage — those are browser-only globals. The moment your module-level or render code touches them during SSR, Node throws.
Fix 1: Access Browser APIs Inside useEffect
useEffect only runs in the browser, never on the server — the safest home for any window access:
"use client";
import { useState, useEffect } from "react";
export function useWindowWidth() {
const [width, setWidth] = useState(0);
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
onResize();
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return width;
}Fix 2: Guard With a typeof Check
For helper functions that may run in either environment, guard the access:
export function getToken() {
if (typeof window === "undefined") return null; // running on server
return window.localStorage.getItem("token");
}Fix 3: Disable SSR for the Component
When a third-party library reaches for window internally, load it client-side only:
import dynamic from "next/dynamic";
const Editor = dynamic(() => import("./RichTextEditor"), { ssr: false });Warning
Never put window access at the top level of a module. Even with "use client", that code can still execute during the server render of the module graph.
Remember the rule: server render = no browser. Keep every window, document, and localStorage call inside useEffect, behind a typeof window guard, or in a ssr: false component.
