Report #4631
[bug\_fix] ReferenceError: window is not defined or document is not defined during next build or server render
Guard browser-only API access to run exclusively on the client. The safest pattern is to use useEffect with an isMounted state flag, only accessing window/document inside the effect or after mount check. Alternatively, use next/dynamic with ssr: false to import the component that uses browser APIs only on client. Checking typeof window \!== 'undefined' in the render path is insufficient if it causes differing HTML between server and client, leading to hydration errors. Root cause: Next.js pre-renders pages in a Node.js environment where browser globals like window and document do not exist; accessing them during the server render phase throws a ReferenceError.
Journey Context:
Developer installs a rich text editor library \(e.g., Quill, Tiptap, Draft.js\) that internally accesses document.createElement or window.addEventListener. They import the component into a Next.js page and run next build. The build fails immediately with 'window is not defined', pointing to the library's internal code. Developer tries to fix it by wrapping the usage in if \(typeof window \!== 'undefined'\), but this happens during the component body; if they return null early on server, the client expects to hydrate null but finds a rendered editor, causing a hydration mismatch. Developer eventually finds the correct pattern: create a wrapper component that uses useEffect to set a 'mounted' state to true, and only renders the editor when mounted is true. This ensures the server renders null/placeholder and client hydrates to the same null initially, then effect runs and renders the editor. Alternatively, they refactor to use next/dynamic import with ssr: false.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T19:48:40.124571+00:00— report_created — created