You're building a Next.js application. You use `localStorage` to save a user's theme, or maybe you import a library like Leaflet or a rich text editor. It works flawlessly when you're clicking around locally.
Then, you run `npm run build`, or try to deploy to Vercel, and the process crashes instantly with:
`ReferenceError: window is not defined`
Why? Because Next.js renders your pages on the server (SSR) before sending them to the client. The server is a Node.js environment. Node.js does not have a `window` object, a `document` object, or `localStorage`. When the server hits that line of code, it panics.
TL;DR - Quick Solutions
- 1Wrap `window` or `localStorage` calls inside a `useEffect` hook.
- 2Check if `typeof window !== 'undefined'` before accessing it.
- 3Use Next.js dynamic imports (`next/dynamic`) with `ssr: false` for third-party browser-only libraries.
Root Causes
Accessing Browser APIs in Component Scope
Calling `window.innerWidth` or `localStorage.getItem()` directly inside the main body of your functional component.
const theme = localStorage.getItem('theme'); // Crashes on server!Importing Client-Side Only Libraries
Importing libraries that immediately access the `window` object upon instantiation (e.g., certain charts, maps, or WYSIWYG editors).
Step-by-Step Fix Guide
Move Code to useEffect
The `useEffect` hook only runs on the client-side *after* the component has mounted. It never runs on the server.
import { useState, useEffect } from 'react';
export default function MyComponent() {
const [theme, setTheme] = useState('light');
useEffect(() => {
// This is safe! It only runs in the browser.
const savedTheme = localStorage.getItem('theme');
if (savedTheme) setTheme(savedTheme);
}, []);
return <div>{theme}</div>;
}Guard with typeof window
If you absolutely must check something outside of a `useEffect` (e.g., inside a utility function), check if the window exists first.
export const getSavedTheme = () => {
if (typeof window !== 'undefined') {
return localStorage.getItem('theme');
}
return 'light'; // Fallback for the server
};Dynamically Import Libraries
If a third-party module crashes the build because it needs the `window`, tell Next.js not to include it in the Server-Side Render.
import dynamic from 'next/dynamic';
// Import the component dynamically and disable SSR
const MapComponent = dynamic(() => import('../components/Map'), {
ssr: false
});
export default function Page() {
return <MapComponent />;
}Is your Next.js deployment failing?
If Vercel or Heroku builds are failing and you're out of time, I provide emergency deployment rescue and Next.js debugging.
Request Deployment RescueRelated Errors
ReferenceError: document is not defined
Same root cause. Replace `document.getElementById` or `document.querySelector` with React `useRef`.
Prevention Strategy
- Always assume your Next.js code will run in a Node.js environment first.
- Rely on React's `useRef` instead of direct DOM manipulation (`document.querySelector`).
Still Stuck With This Issue?
Send your exact error message or deployment issue. I'll respond with a targeted fix.
Need a Deeper Fix?
Describe your full project issue below and I'll get back to you with a targeted fix.