ReferenceError: window is not defined
Code that only works in a browser ran on the server, where there is no window, document, or localStorage.
Also appears as
- ReferenceError: document is not defined
- ReferenceError: localStorage is not defined
- ReferenceError: navigator is not defined
- TypeError: localStorage.getItem is not a function
Prompt to fix it
My app fails with "ReferenceError: window is not defined" (or document or localStorage is not defined) during server rendering. Find every place that touches window, document, localStorage, or navigator during render or when a module is imported. Move those reads into useEffect or event handlers, give state a default that matches the server so hydration stays clean, and load browser-only libraries with next/dynamic and ssr: false from a Client Component. Don’t turn off server rendering for the whole app or wrap rendered output in typeof window checks.
Paste into Layerwise or any AI coding tool.
What it means
Next.js and other server-rendering frameworks run your components on the server first to produce HTML. The server runs Node.js, which has no browser around it, so browser globals such as window, document, localStorage, and navigator don’t exist there. The first line that touches one throws this error.
Adding 'use client' doesn’t prevent it. Client Components still render once on the server for the first HTML, then again in the browser. Only code inside useEffect and event handlers is guaranteed to run in the browser alone.
Newer Node.js versions have a localStorage of their own that only works with the --localstorage-file flag, so the same bug can show up as localStorage.getItem is not a function. Check typeof window instead of typeof localStorage, and keep that check out of render so it doesn’t cause a hydration mismatch.
Common causes
- A component reads
window,localStorage, ordocumentin its body, which also runs on the server. - A module touches a browser global when it’s imported, for example
const saved = localStorage.getItem('theme')at the top of a file. - A browser-only library, such as a map, chart, or rich text editor, is imported into a page the server renders.
- A shared helper used by server code assumes it always runs in the browser.
How to fix it
- Read the stack trace to find the file and line that touched the browser global.
- Move that code into
useEffector an event handler, which run only in the browser. - For values needed while rendering, start with a default that matches the server and update it in
useEffectafter the page loads. - Load browser-only libraries with
next/dynamicand{ ssr: false }, called from a Client Component. - In helpers that run in both places, check
typeof window !== 'undefined'before using browser APIs.
Example fix
'use client';
import dynamic from 'next/dynamic';
import { useEffect, useState } from 'react';
// Browser-only library: skip server rendering for this one component.
const MapView = dynamic(() => import('./map-view'), { ssr: false });
export function Dashboard() {
// Same value on the server and on the first browser render.
const [theme, setTheme] = useState('light');
useEffect(() => {
setTheme(localStorage.getItem('theme') ?? 'light');
}, []);
return <MapView theme={theme} />;
}Related errors
All errorsHydration failed
The HTML the server sent didn’t match what React rendered first in the browser, so React threw that part away and rebuilt it on the client.
You’re importing a component that needs useState
A Next.js Server Component used a browser-side React feature like useState, which only works in files marked as Client Components.
X is not defined
The code used a name JavaScript can’t find anywhere, usually because of a missing import, a typo, or a Node-only global like process in the browser.
Too many re-renders
A component changes its own state while it renders, so React renders it again and again until it stops the loop.
Build with an agent that checks its work
The Layerwise agent clicks through the live preview and reads the console, so it catches errors before you do.