✋ Update: This post was originally published on my blog decodingweb.dev, where you can read the latest version for a 💯 user experience. ~reza
This guide explains why ReferenceError: document is not defined occurs and how you can fix it.
let element = document.querySelector('#usernameLabel')
^
ReferenceError: document is not defined
The most common reason for "document is not defined" error is that your JavaScript code is trying to access the document
object in a non-browser environment, such as Node.js.
A quick intro to the document object:
Whenever you access a web page via the web browser, the browser's renderer fetches and parses the HTML code (along with the assets) and stores its elements (headings, paragraphs, images, form elements, etc.) into an internal data structure called DOM (Document Object Model).
DOM is then turned into Render Tree (consisting of visible elements and their styles), and through a complicated process, the code is turned into pixels - what you see as a user.
Additionally, the web browser exposes an interface to the DOM tree called DOM API. The DOM API lets you modify the page programmatically via JavaScript to do things like:
- Set the page title on the fly
- Add/remove HTML element(s)
- Manage CSS styles
- Add/remove CSS classes
- and a lot more!
And the entry point to this powerful API is the document
object.
What causes ReferenceError: document is not defined
The first thing to know is that the DOM API is a web browser component and not a part of JavaScript. JavaScript only enables you access this object (like any other object).
Here are the possible scenarios that "ReferenceError: document is not defined" error might happen:
- A reference to the
document
object in Node.js - Server-side Rendering (SSR)
- Casing might be off.
Let's see how we can fix them.
How to fix "document is not defined" error
In Node.js: As mentioned earlier, the document object is a browser feature and not a part of JavaScript. On the other hand, when there's no browser, there's no document object.
With Node.js, you must review the code and figure out why you're accessing a browser object in a server-side code in the first place? Maybe it's just there because you borrowed the code from another file?
However, if your code is used by both the front end and the server, you can place the reference to the document
inside a conditional statement - to ensure it'll only run by the browser's JavaScript engine.
To do this, you can place it inside a conditional statement and check for the availability of the document
object:
// This code only runs on the browser
if (typeof document !== 'undefined') {
let element = document.querySelector('.class-name')
// Manipulating the DOM here
}
Serverside-rendering (SSR): The "document is not defined" error might also occur when using static site generators, such as Nuxt.js, Next.js, or Gatsby.
A quick fix - as suggested above - is to place any reference to the document object inside a conditional statement - to exclude it from the server-side rendering.
If you use Next.js, you can place your DOM-related code inside a useEffect
hook. The callback passed to useEffect
runs after the component is mounted (and painted).
The example below is from React's documentation:
// useeffect-example
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = 'You clicked ' + count + ' times';
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
How about Nuxt.js? If you're using Nuxt.js with Vue, and the error is raised in one of your components, you can wrap your component inside a <client-only>
component, and the error will go away.
<client-only>
guarantees that your component is only rendered on the client side.
// nuxtjs-client-only.js
<template>
<div>
<client-only placeholder=" loading...">
<your-component>
</client-only>
</div>
</template>
Additionally, you can access DOM from within the mounted
Vue lifecycle hook.
Or maybe it's just a typo? It's also worth checking the document
object is all lowercase and not capitalized!
// document-reference-case.js
// ⛔ Wrong
Document.querySelector('.class-name')
// ✅ Correct
document.querySelector('.class-name')
Wrapping up
In this quick guide, you learned the document
object is a browser feature and should always be used in a browser environment.
The most common reason for this error is that the code tries to access the document
object in a non-browser environment, such as Node.js.
Two common way to avoid such errors is to use a conditional statement to ensure the code only runs in the browser.
Alternatively, you can use hook mechanisms in case you're using an SSR framework such as Next or Nuxt.js.
I hope this guide provided the answer you were looking for.
Thanks for reading!
❤️ You might like:
Top comments (0)