How to solve the document is not defined error

By

Learn how to fix the 'document is not defined' error in Node.js or Next.js: document is browser-only, so guard it with a typeof window check.

~~~

The “ReferenceError: document is not defined” error happens when you access document in Node.js or in the server-side part of a tool like Next.js. The fix is to make sure the code that uses document only runs in the browser.

document is an object that’s made available by the browser, and it’s not available in a server-side JavaScript environment.

I describe the document object is details in my extensive DOM Document Object Model guide.

Why does this error happen?

The browser gives you document as the entry point to the page content. A server has no page, so the object does not exist there.

This line works in the browser and crashes in Node:

document.title = 'Dashboard'
// ReferenceError: document is not defined

Fixing it in Node.js

With Node.js in particular there’s no way to workaround the problem - you must find the particular place where document is used, and revisit the code to figure out why you are accessing the document object.

You are running frontend code in a backend environment.

A common cause is importing a library that was written for the browser, like a charting or animation library. Move that code to the frontend, or look for a server-friendly alternative.

Fixing it in Next.js

In Next.js 16, new projects use the App Router. Its pages and layouts are Server Components by default, and a Server Component only runs on the server, so document never exists inside it. Move the code that needs document into a Client Component, by adding 'use client' at the top of its file.

That alone isn’t enough, though. Next.js still prerenders Client Components to HTML on the server, so inside them document must sit in a useEffect() callback or behind the check below.

If your app uses the older Pages Router, the same component code also runs in two places. It runs in the browser when you navigate to a page using a link, and it runs server-side when Next.js renders the page, for example when you use getServerSideProps().

In both cases, you can limit the reference to document into a conditional that checks if the window object is available, like this:

if (typeof window !== 'undefined') {
  //here `window` is available, so `window.document` (or `document`) is available too
}

And this will fix your problem, since you only run anything inside the conditional in a browser environment. Don’t use it to decide what a component renders, though: the server and the browser would produce different HTML, and you’d get a hydration error.

Alternatively, you can put the code inside a useEffect() callback. Effects never run during server-side rendering, only in the browser after the component mounts:

useEffect(() => {
  document.title = 'Dashboard'
}, [])

In the App Router, this goes in a file that starts with 'use client', because hooks don’t work in Server Components.

Be careful with the check

Write the check exactly as typeof window !== 'undefined'.

Checking window !== undefined looks equivalent, but it’s not. Referencing an identifier that doesn’t exist throws a ReferenceError, so on the server that check crashes with “window is not defined” - the same class of error you’re trying to fix. The typeof operator is safe to use on names that don’t exist.

The same fix applies to the “window is not defined” and “localStorage is not defined” errors. They all mean the same thing: browser-only code is running on the server.

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about js: