"document is not defined" is a ReferenceError thrown when code tries to access the browser's document object in a runtime where it doesn't exist.
What "document is not defined" Means
The document object is the entry point to the DOM in browsers. When JavaScript executes outside a browser environment — or before the browser has finished parsing — that object simply doesn't exist, so referencing it throws this error.
Why It Happens
The two most common causes:
- Server-side rendering (SSR): In Node.js or Next.js server components, there is no DOM. Code that references
documentduring server-side execution crashes immediately. - Module scope confusion: In Next.js or Vite, code at the top level of a module runs during import. If that code touches
documentbefore the browser has loaded the page, you get this error.
Example Code That Triggers It
// This file runs both on the server and in the browser (e.g., a Next.js page)
const theme = document.querySelector('html').dataset.theme;
console.log(theme);
Run that in Node.js directly:
node script.js
# ReferenceError: document is not defined
How to Fix It
Guard the access so it only runs in the browser:
// Safe version — checks for browser environment
const getTheme = () => {
if (typeof window === 'undefined') {
return 'light'; // default on server
}
return document.querySelector('html').dataset.theme;
};
In React/Next.js, use useEffect or dynamic imports with ssr: false to defer DOM access until after hydration:
import { useEffect, useState } from 'react';
function Theme() {
const [theme, setTheme] = useState('light');
useEffect(() => {
setTheme(document.querySelector('html').dataset.theme);
}, []);
return <p>{theme}</p>;
}
The fix works because useEffect runs exclusively in the browser after the component mounts — never on the server.
Common Mistakes That Cause This
-
Calling
documentat module top-level in a universal component. Any file imported by both server and client bundles will execute that line on the server. Move DOM access inside functions, effects, or event handlers. -
Using
documentdirectly in a Next.jsgetServerSidePropsor API route. These run purely in Node.js. If you need browser data there, pass it from the client via a fetch call instead.
When Should You Worry About This?
You should worry when this error appears in client-side only code — that means your build tooling is misconfigured. A Next.js page that crashes with "document is not defined" during client navigation indicates a module-level side effect that shouldn't exist. In plain browser JavaScript (no framework), this error means you're loading the script in the <head> without defer — move it to the end of <body> or add defer to the script tag.
The one thing to check first: search your codebase for document at the top level of any file that gets imported by both server and client bundles. That's the culprit nine times out of ten.