# JavaScript: “ReferenceError: document is not defined”

> ReferenceError: document is not defined in Jest, Vitest, a Node.js script or server rendering. Use a jsdom test environment, or run DOM code in the browser.

Canonical: https://app369.com/fix/document-is-not-defined/
Last updated: 2026-11-08

A test, a Node.js script or a server-rendered page fails with ReferenceError: document is not defined. document exists only in a browser page. Test runners default to Node.js, plain Node.js scripts have no page, and frameworks such as Next.js and Nuxt run components on the server first.

**Short answer**

In Jest, set testEnvironment to jsdom (and install jest-environment-jsdom, which Jest 28 and later no longer bundle); in Vitest, set environment to jsdom or happy-dom. In a Node.js script that needs to read HTML, use a parser such as jsdom or cheerio instead of document. In Next.js, Nuxt or SvelteKit, move the DOM code into useEffect, onMounted or onMount so it only runs in the browser.

## **Where the code runs, and the fix**

1. 01 ### **Jest tests running in the Node.js environment**
  > “The default environment in Jest is a Node.js environment. If you are building a web app, you can use a browser-like environment through jsdom instead.” [**Jest, Configuration: testEnvironment**](https://jestjs.io/docs/configuration) Tests for components that touch the DOM need a simulated browser. Set it for the whole project, or per file with a docblock at the top.```
npm install -D jest-environment-jsdom

// jest.config.js
module.exports = { testEnvironment: 'jsdom' };

/**
 * @jest-environment jsdom
 */
```
2. 02 ### **Vitest tests in the default environment** Vitest also defaults to Node.js. Install jsdom or happy-dom and set the environment in the config, or per file with a comment.```
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({ test: { environment: 'jsdom' } });

// or at the top of one test file
// @vitest-environment jsdom
```
3. 03 ### **A Node.js script that parses HTML** Node.js has no page and no document. To read or change HTML in a script, load it into a parser.```
import { JSDOM } from 'jsdom';
const { document } = new JSDOM(html).window;
const title = document.querySelector('h1')?.textContent;
```
4. 04 ### **Server rendering in Next.js, Nuxt or SvelteKit** Components render on the server before the browser. Read document inside useEffect (React), onMounted (Vue) or onMount (Svelte), or load browser-only components on the client only.```
useEffect(() => {
  document.title = \`${count} new messages\`;
}, [count]);
```

Checked against the vendor’s docs on November 8, 2026: [Jest: Configuration (testEnvironment)](https://jestjs.io/docs/configuration).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**Next.js “window is not defined”**](https://app369.com/fix/nextjs-window-is-not-defined/) · [**JavaScript “x is not defined”**](https://app369.com/fix/javascript-is-not-defined/) · [**“self is not defined”**](https://app369.com/fix/self-is-not-defined/)

Questions

## **More on this error**

<details>

<summary>**Should I check typeof document before using it? **+</summary>

For shared utility code, typeof document !== 'undefined' is a safe guard. In components, it can make the server and browser render different HTML, so prefer running DOM code after mount.

</details>

<details>

<summary>**jsdom or happy-dom? **+</summary>

jsdom is more complete; happy-dom is faster but lacks some APIs. Start with jsdom and switch only if test speed matters and your tests pass.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)