ReferenceError: window is not defined
Filed under Next.js & React errors
Next.js renders your page on the server first, and the server has no browser — so any code that touches window, document, or localStorage during that render crashes.
Fastest route
Give your AI this prompt
Copy it, replace the bracketed part with your error, and send it.
Preview the prompt
My Next.js app crashes with: "ReferenceError: window is not defined". The stack trace points at: [paste the top few lines of the stack trace]. Here is the component or file named in the trace: [paste the code]. Please: 1) find the exact line that touches window, document, or localStorage during render or at module level, 2) if it is my code, move it into a useEffect — or, if a whole component is browser-only, switch its import to next/dynamic with ssr: false and make sure the importing file is a Client Component, 3) if only one value is read during render, guard it with typeof window !== "undefined" and choose a sensible server-side default, 4) make sure the server render and the first browser render produce the same output so this does not turn into a hydration error. Constraints: do not remove the feature that uses window, do not wrap the whole page in ssr: false, and show me the before/after of just the lines you change.
Fix it yourself
Follow the repair
- Read the stack trace and find whose code touched window. The error names the file and line. If it is your component, one of the next three fixes applies directly. If the trace points into node_modules, a library is the culprit and step 6 is your fix — do not go rearranging your own components first.
- Move browser-only work into useEffect. Effects run only in the browser, after the page exists, so anything browser-shaped is safe inside one:
Reading localStorage, measuring elements, attaching scroll listeners, initializing browser libraries — all of it belongs here.const [width, setWidth] = useState(0); useEffect(() => { // window exists here — this never runs on the server setWidth(window.innerWidth); }, []); - For a whole browser-only component, lazy-load it with ssr: false. Some components are browser-only by nature — an interactive map, a rich text editor. Tell Next.js not to render that one on the server at all:
The file doing this import must be a Client Component —import dynamic from "next/dynamic"; const Map = dynamic(() => import("./Map"), { ssr: false });"use client"at the top — or Next.js will refuse the ssr: false option. - Guard one-off reads with typeof window. For a single value needed during render, branch on where you are:
The false branch is what the server renders, so pick a default that makes sense on its own. Treat guards as a scalpel, not a strategy — if you are writing them everywhere, the code wants to live in an effect instead.const isBrowser = typeof window !== "undefined"; const startOpen = isBrowser ? window.innerWidth > 900 : false; - Keep the server render and the first browser render identical. If a guarded value changes what is on the screen, the HTML from the server and the first browser render will disagree, and this error trades itself for a hydration error. The stable pattern: render the default first, set a mounted flag in useEffect, then swap in the browser value. One extra render, no mismatch.
- Wrap offending libraries rather than editing your code around them. If the stack trace names a package, find the component of yours that imports it and apply step 3's dynamic import at that spot. The library never loads on the server, its window access happens only in the browser, and your other components stay untouched.
Why this happensOptional explanation
Next.js renders every page twice. First it renders on the server to produce the initial HTML — that is what makes pages load fast and show up in Google. Then the same code runs again in the visitor's browser to make the page interactive. The catch: a server is not a browser. It has no window, no document, no localStorage. Any line of code that touches those things while the component function itself is running hits the server render first, and the server answers ReferenceError: window is not defined.
The code itself is usually fine — it would run perfectly in a plain browser app. It is simply executing at a moment when no browser exists yet. Code inside event handlers (onClick and friends) never causes this because clicks only happen in the browser. Code inside useEffect never causes it either, because effects only run in the browser. The crash comes from browser-only code sitting in the body of the component, at the top level of a file, or inside a library that runs the moment it is imported.
- Window or document in the component body: reading window.innerWidth, adding a listener, or querying the DOM while the component renders.
- localStorage read during render: loading a saved setting or token as the component runs — a browser-only API, same crash.
- A browser-only library: maps, charting, carousel, or editor packages that touch window the moment they load — the stack trace points into node_modules.
- Module-level code: anything at the top of a file, outside any function, runs on import — including on the server.
Quick answers
I get the same error for localStorage. Same fix?
localStorage is a browser API, so it has the same problem as window — it does not exist on the server. Read it inside a useEffect, or inside an event handler, and keep the first render free of it. If the value affects what is displayed, render a placeholder first and fill it in once the effect runs.
What does ssr: false actually do?
Dynamic import tells Next.js to skip rendering that component on the server entirely and only load it in the browser — the clean fix for components that are browser-only by nature, like maps and chart libraries. It only works inside a Client Component, so the file needs the "use client" directive at the top.
My page used to work. Did Next.js change something?
Yes — and in recent Next.js versions this error nearly always means browser-only code, not a framework bug. Rendering on the server first is how Next.js makes pages fast and searchable; the error is the framework telling you the code assumed a browser before one existed.