ReferenceError: window is not defined

Code that only works in a browser ran on the server, where there is no window, document, or localStorage.

Terminal and Next.js error overlay
ReferenceError: window is not defined

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, or document in 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

  1. Read the stack trace to find the file and line that touched the browser global.
  2. Move that code into useEffect or an event handler, which run only in the browser.
  3. For values needed while rendering, start with a default that matches the server and update it in useEffect after the page loads.
  4. Load browser-only libraries with next/dynamic and { ssr: false }, called from a Client Component.
  5. 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} />;
}
All errors
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used: - A server/client branch `if (typeof window !== 'undefined')`. - Variable input such as `Date.now()` or `Math.random()` which changes each time it's called. - Date formatting in a user's locale which doesn't match the server. - External changing data without sending a snapshot of it along with the HTML. - Invalid HTML tag nesting. It can also happen if the client has a browser extension installed which messes with the HTML before React loaded. https://react.dev/link/hydration-mismatch

Hydration 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 module that depends on `useState` into a React Server Component module. This API is only available in Client Components. To fix, mark the file (or its parent) with the `"use client"` directive. Learn more: https://nextjs.org/docs/app/api-reference/directives/use-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.

ReferenceError: useState is not defined

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. React limits the number of renders to prevent an infinite loop.

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.