Next.js error
ReferenceError: window Is Not Defined
The error
ReferenceError: window is not definedYour code ran on a server, where there is no browser. The fix depends on whether the value is needed to render or only after the page is interactive.
window, document, localStorage and navigator are provided by a
browser. A Server Component, a route handler, generateStaticParams, and the
server pass of a Client Component all run in Node, where none of them exist.
The error is telling you the code ran somewhere it did not expect to. Which fix applies depends on when the value is needed.
If it is needed only after the page is interactive
Move it into an effect. This covers measuring an element, reading
localStorage, subscribing to a resize or scroll, setting up an observer -
anything that is a consequence of being in a browser rather than an input to
the markup.
'use client';
const [width, setWidth] = useState<number | null>(null);
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
onResize();
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);Effects do not run on the server, so there is nothing to guard.
If it is needed to render
You have a decision, not a workaround. Render a stable value on both passes and correct it after mount:
const [theme, setTheme] = useState<'light' | 'dark'>('light'); // both passes agree
useEffect(() => setTheme(readStoredTheme()), []);The first paint shows the default; the stored value arrives immediately after. If that flash is unacceptable - a theme is the usual case - the value has to reach the server instead, as a cookie read in a Server Component, so it is in the HTML from the start.
What you must not do is branch on typeof window during render. The server
renders one branch and the browser renders the other, which is
a hydration mismatch by
construction.
If it is a library that touches window on import
Some packages read window at module scope, so importing them is enough to
throw. Import them lazily instead:
'use client';
import dynamic from 'next/dynamic';
const Map = dynamic(() => import('./map'), { ssr: false });Be deliberate here. ssr: false means the component is absent from the HTML,
so it is invisible to a crawler and arrives after hydration for everyone else.
That is the correct trade for a map widget and the wrong one for your main
content.
Finding it during the build
next build prerenders every static route, which is why the build is where
this usually surfaces. The error names the module but not always the route; if
the stack is unhelpful, the route list in the build output narrows it - the
first route that fails is the one importing the offending module.
A related shape worth knowing: the same code in middleware fails for a different reason. The edge runtime is not Node either, so Node APIs are missing there even though you are on a server.
