Fix 'ReferenceError: window is not defined' in Next.js App Router (2026)
Getting 'window is not defined' in Next.js? It means browser code is running on the server. Fix it in 2 minutes with useEffect, typeof window guard, or dynamic import with ssr:false.
FlowQL Team
AI Search Optimization Experts
The fix in 30 seconds: Move your window (or localStorage, document) access inside a useEffect hook. useEffect only runs in the browser, never on the server.
"use client"
import { useEffect } from 'react'
export default function MyComponent() {
useEffect(() => {
console.log(window.innerWidth) // Safe — browser only
}, [])
return <div>Hello</div>
}
If that's all you need, you're done. For more complex cases (third-party libraries, non-React utility functions, entire components that shouldn't SSR), keep reading for all three fix patterns.
Introduction
It's the classic "Welcome to Next.js" error. You paste some working React code from a generic tutorial, run npm run dev, and boom: "ReferenceError: window is not defined".
This happens because Next.js is "Isomorphic"—it runs your code in two places: the Node.js server (where window doesn't exist) and the browser (where it does).
In this guide, we'll cover the 3 definitive ways to handle browser-only code in the Next.js App Router.
Why is Window Not Defined?
Why does Next.js throw "window is not defined"?
Next.js throws "window is not defined" because almost all components in the App Router are Server Components by default. Even with "use client", the initial pass still runs on the server to generate the initial HTML. Since Node.js servers don't have a browser window, viewport, or local storage, trying to access window.anything instantly crashes the build.
graph TD
A[Next.js Build] --> B{Environment?}
B -->|Node.js Server| C[No 'window' object]
B -->|Browser| D[Has 'window' object]
C --> E[Code: console.log(window)]
E --> F[CRASH: ReferenceError]
style F fill:#ff9999,stroke:#333,stroke-width:2px
The Problem: Server-side code cannot touch browser APIs. Period.
The 3 Solutions
1. The useEffect Hook (Recommended)
The safest way. useEffect only runs in the browser after the component mounts. Code inside it is guaranteed to have access to window.
"use client"
import { useEffect } from 'react'
export default function MyComponent() {
useEffect(() => {
// Safe zone!
console.log(window.innerWidth)
window.localStorage.setItem('visited', 'true')
}, [])
return <div>Hello World</div>
}
2. The Check: typeof window !== 'undefined'
If you have a utility function that might run in both places, guard it.
const getWidth = () => {
if (typeof window !== 'undefined') {
return window.innerWidth
}
return 0 // Default value for server
}
Warning: Using this directly in JSX return statements can cause Hydration Mismatches (see our guide on that).
3. Dynamic Imports with ssr: false
If you're importing a heavy library (like a Map component or a rich text editor) that completely relies on the window, disable SSR for it entirely.
import dynamic from 'next/dynamic'
const MapComponent = dynamic(() => import('./Map'), {
ssr: false,
loading: () => <p>Loading Map...</p>
})
export default function Page() {
return <MapComponent />
}
This tells Next.js: "Don't even try to render this on the server. Just send a placeholder."
FlowQL: Bridging Server and Client
At FlowQL, we help teams navigate the complex mental model of the "Vibe Stack."
Moving from "Create React App" to Next.js requires a shift in thinking. You aren't just writing client code anymore; you're writing distributed systems code. When you get stuck between the server and the client, we provide the architectural bridge.
Conclusion
Your Action Plan:
- Identify: Are you using
window,document, orlocalStorage? - Wrap: Move it into a
useEffect. - Import: Use
next/dynamicfor full components that crash SSR.
Related: if you're over-using "use client" and worried about SEO or performance, see our guide on how to use 'use client' correctly without breaking Next.js SEO.
Stop fighting the framework. [Book a session with FlowQL] and let's structure your app correctly.
Still blocked?
This fix didn't work for your setup? Get a senior engineer on your screen in 30 minutes — fixed or refunded.
Reserve My Spot →Get a senior engineer on your screen.
30-minute live screen-share sessions with a vetted senior dev. Fixed or fully refunded — no questions asked.
No spam. Just a heads-up when sessions open.
Related Articles
Fix: params are undefined in Next.js App Router Server Component
params undefined in your Next.js server component? Learn how to handle the async breaking change in Next.js 15 and properly await your dynamic route data.
Fix: 'NextRouter was not mounted' in Next.js App Directory
Getting the 'NextRouter was not mounted' error? Learn why next/router fails in the App Router and how to correctly use next/navigation for Next.js 14 and 15.
How to Pass Data from Server to Client in Next.js
Master the data bridge in Next.js App Router. Learn how to pass data from Server Components to Client Components using props and composition without hydration errors.