window exists only in browsers. This error means code that touches window (or document, localStorage, navigator) executed somewhere else: Node.js, a test runner, or the server pass of server-side rendering in frameworks like Next.js, where components render once on the server to produce HTML and again in the browser. The same component can be correct in the browser and still crash the render if the browser-only line runs during that first server pass.
window touched at the top level of a file, in a class field, or in a variable initializer runs at import time, before any component or effect, and is the most common hiding place.useEffect runs only in the browser), or the framework's client-only lifecycle hook. Rendering must be possible without a window; effects are where windows exist.if (typeof window !== 'undefined') { ... } around the specific access, used sparingly for libraries and initialization code, not wrapped around whole components.dynamic(() => import('lib'), { ssr: false }); elsewhere, a dynamic import() inside an effect, so the server never evaluates the library at all.Why does it work after a refresh sometimes and crash on first load? Client-side navigation renders only in the browser, so the page works; a full load or refresh renders on the server first and hits the error. That asymmetry is the signature of an SSR-only bug.
Is checking typeof window everywhere the right pattern? No. It is a targeted tool for initialization and library code. Needing it in many components usually means browser-only logic belongs in effects or a client-only wrapper component.
Does document fail the same way? Yes, document, localStorage, sessionStorage, navigator and location are all browser globals with the same server problem and the same fixes.