search
Javascript star Featured

Fix: document is not defined error in JavaScript

Learn how to fix the 'document is not defined' error in JavaScript applications. This comprehensive guide covers server-side rendering, Node.js, and browser compatibility.

person By Gautam Sharma
calendar_today January 2, 2026
schedule 14 min read
JavaScript document Node.js SSR Error Frontend Backend

The ‘document is not defined’ error is a common JavaScript issue that occurs when trying to access the document object in environments where it doesn’t exist, such as Node.js servers or during server-side rendering. This error typically happens when browser-specific code runs in non-browser environments.

This comprehensive guide explains what causes this error, why it happens, and provides multiple solutions to fix it in your JavaScript projects with clean code examples and directory structure.


What is the document is not defined Error?

The “document is not defined” error occurs when:

  • Browser-specific code runs in Node.js environment
  • Server-side rendering attempts to access browser APIs
  • Code is executed before DOM is available
  • Universal/isomorphic JavaScript runs in wrong context
  • Testing environments don’t provide browser APIs

Common Error Messages:

  • ReferenceError: document is not defined
  • document is not defined
  • Uncaught ReferenceError: document is not defined
  • Cannot access 'document' before initialization
  • document is not available in this environment

Understanding the Problem

The document object is part of the browser’s DOM API and is not available in server-side environments like Node.js. This creates compatibility issues when writing universal JavaScript code that needs to run in both browser and server environments.

Typical JavaScript Project Structure:

my-universal-app/
├── package.json
├── webpack.config.js
├── src/
│   ├── client/
│   │   ├── main.js
│   │   ├── components/
│   │   │   └── app.js
│   │   └── utils/
│   │       └── domHelpers.js
│   ├── server/
│   │   ├── server.js
│   │   ├── ssr.js
│   │   └── controllers/
│   │       └── pageController.js
│   ├── shared/
│   │   ├── constants.js
│   │   └── helpers.js
│   └── config/
├── public/
└── dist/

Solution 1: Check for Browser Environment

The most common solution is to check if the document object exists before using it.

❌ Without Environment Check:

// ❌ This will cause "document is not defined" in Node.js
const element = document.getElementById('myElement'); // ❌ Error in Node.js
const title = document.title; // ❌ Error in Node.js
const cookie = document.cookie; // ❌ Error in Node.js

✅ With Environment Check:

utils/domHelpers.js:

// ✅ Safe DOM access with environment check
class DOMHelpers {
  static isBrowser() {
    return typeof window !== 'undefined' && typeof document !== 'undefined';
  }

  static getElementById(id) {
    if (!this.isBrowser()) {
      console.warn('Cannot access DOM in non-browser environment');
      return null;
    }
    return document.getElementById(id);
  }

  static setTitle(title) {
    if (this.isBrowser()) {
      document.title = title;
    }
  }

  static getCookie(name) {
    if (!this.isBrowser()) {
      return null;
    }
    // ✅ Safe cookie access
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
  }

  static addClass(elementId, className) {
    if (!this.isBrowser()) {
      return false;
    }
    const element = document.getElementById(elementId);
    if (element) {
      element.classList.add(className);
      return true;
    }
    return false;
  }
}

// ✅ Usage examples
if (DOMHelpers.isBrowser()) {
  const element = DOMHelpers.getElementById('myElement');
  if (element) {
    element.innerHTML = 'Hello World';
  }
}

Solution 2: Use typeof Check

Use typeof to safely check for the existence of browser APIs.

❌ Without typeof Check:

// ❌ Will throw error in Node.js
if (document.readyState === 'complete') {
  console.log('DOM is ready');
}

✅ With typeof Check:

// ✅ Safe check using typeof
if (typeof document !== 'undefined' && document.readyState === 'complete') {
  console.log('DOM is ready');
}

// ✅ Alternative: Check multiple browser APIs
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
  // ✅ Safe to use browser APIs
  const title = document.title;
  const element = document.createElement('div');
}

Solution 3: Server-Side Rendering with Document Fallback

For server-side rendering, provide fallbacks for browser-specific code.

❌ Without SSR Consideration:

// ❌ This won't work during SSR
const userAgent = navigator.userAgent; // ❌ navigator is undefined in Node.js
const cookie = document.cookie; // ❌ document is undefined in Node.js

✅ With SSR Consideration:

server/ssr.js:

// ✅ Server-side rendering with browser API fallbacks
class SSRRenderer {
  static renderPage(req, res, component) {
    // ✅ Get user agent from request headers instead of navigator
    const userAgent = req.get('User-Agent') || '';
    
    // ✅ Get cookies from request instead of document
    const cookies = req.cookies || {};
    
    // ✅ Render component with server-provided data
    const html = component.render({
      userAgent,
      cookies,
      isServer: true
    });
    
    res.send(html);
  }
  
  static getClientScript() {
    // ✅ Provide client-side initialization script
    return `
      <script>
        // ✅ Client-side code that runs after DOM is available
        if (typeof document !== 'undefined') {
          window.APP_DATA = ${JSON.stringify(this.getServerData())};
          // ✅ Initialize client-side functionality
          initializeApp();
        }
      </script>
    `;
  }
  
  static getServerData() {
    // ✅ Data that can be safely provided during SSR
    return {
      timestamp: Date.now(),
      userAgent: 'server-rendered',
      isServer: true
    };
  }
}

Solution 4: Use JSDOM for Testing

For testing environments, use JSDOM to provide browser-like APIs.

package.json:

{
  "name": "my-universal-app",
  "version": "1.0.0",
  "scripts": {
    "test": "jest",
    "start": "node server/server.js",
    "build": "webpack"
  },
  "dependencies": {
    "express": "^4.18.0"
  },
  "devDependencies": {
    "jest": "^29.0.0",
    "jsdom": "^22.0.0",
    "@babel/preset-env": "^7.0.0"
  }
}

tests/domHelpers.test.js:

// ✅ Test with JSDOM providing browser APIs
const { JSDOM } = require('jsdom');

describe('DOM Helpers', () => {
  beforeAll(() => {
    // ✅ Set up JSDOM environment
    const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
      url: 'http://localhost',
      pretendToBeVisual: true,
      resources: 'usable'
    });
    
    global.window = dom.window;
    global.document = dom.window.document;
    global.navigator = dom.window.navigator;
  });

  afterAll(() => {
    // ✅ Clean up global variables
    delete global.window;
    delete global.document;
    delete global.navigator;
  });

  test('should access document safely', () => {
    // ✅ Now document is available for testing
    const element = document.createElement('div');
    expect(element).toBeDefined();
  });
});

Solution 5: Conditional Browser Code Execution

Execute browser-specific code only when in browser environment.

utils/browser.js:

// ✅ Browser detection and conditional execution
class BrowserUtils {
  static isBrowser() {
    return typeof window !== 'undefined' && typeof document !== 'undefined';
  }

  static isNode() {
    return typeof process !== 'undefined' && 
           process.versions && 
           process.versions.node;
  }

  static isWebWorker() {
    return typeof importScripts === 'function';
  }

  static executeInBrowser(callback, fallback = null) {
    if (this.isBrowser()) {
      return callback();
    } else if (fallback) {
      return fallback();
    }
    return null;
  }

  static getBrowserInfo() {
    if (!this.isBrowser()) {
      return {
        isBrowser: false,
        userAgent: 'server',
        platform: 'server'
      };
    }

    return {
      isBrowser: true,
      userAgent: navigator.userAgent,
      platform: navigator.platform,
      language: navigator.language
    };
  }

  static setDocumentTitle(title) {
    return this.executeInBrowser(() => {
      document.title = title;
    }, () => {
      console.log(`Document title would be: ${title} (in browser)`);
    });
  }
}

// ✅ Usage examples
const browserInfo = BrowserUtils.getBrowserInfo();
console.log('Running in browser:', browserInfo.isBrowser);

BrowserUtils.setDocumentTitle('My App');

Solution 6: Universal Module Pattern

Create modules that work in both browser and server environments.

shared/universalModule.js:

// ✅ Universal module that works in both environments
(function (global, factory) {
  if (typeof exports === 'object' && typeof module !== 'undefined') {
    // ✅ Node.js environment
    module.exports = factory();
  } else if (typeof define === 'function' && define.amd) {
    // ✅ AMD environment
    define(factory);
  } else {
    // ✅ Browser environment
    global.UniversalModule = factory();
  }
})(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : global || self, function () {
  return (function () {
    'use strict';

    // ✅ Check for browser environment
    const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';

    function getDeviceInfo() {
      if (isBrowser) {
        return {
          userAgent: navigator.userAgent,
          platform: navigator.platform,
          cookieEnabled: navigator.cookieEnabled,
          language: navigator.language
        };
      } else {
        return {
          userAgent: 'server',
          platform: 'server',
          cookieEnabled: false,
          language: 'en-US'
        };
      }
    }

    function getDocumentInfo() {
      if (isBrowser) {
        return {
          title: document.title,
          url: document.URL,
          referrer: document.referrer
        };
      } else {
        return {
          title: 'Server Rendered',
          url: 'server://',
          referrer: 'server'
        };
      }
    }

    // ✅ Public API
    return {
      isBrowser,
      getDeviceInfo,
      getDocumentInfo
    };
  })();
});

Solution 7: Use Environment-Specific Code Splitting

Split code based on environment to avoid browser-specific code in Node.js.

src/client/main.js:

// ✅ Browser-specific code
import { App } from './components/app.js';

class ClientApp {
  constructor() {
    this.init();
  }

  init() {
    // ✅ Safe to use document here since this only runs in browser
    const appContainer = document.getElementById('app');
    if (appContainer) {
      const app = new App();
      appContainer.innerHTML = app.render();
    }
  }
}

// ✅ Initialize when DOM is ready
if (typeof document !== 'undefined') {
  document.addEventListener('DOMContentLoaded', () => {
    new ClientApp();
  });
}

src/server/server.js:

// ✅ Server-specific code
const express = require('express');
const path = require('path');

const app = express();

// ✅ Serve static files
app.use(express.static(path.join(__dirname, '../../public')));

// ✅ API routes
app.get('/api/data', (req, res) => {
  res.json({ message: 'Server data' });
});

// ✅ SSR route
app.get('*', (req, res) => {
  // ✅ Server-side rendering without accessing document
  const html = `
    <!DOCTYPE html>
    <html>
      <head>
        <title>Server Rendered App</title>
      </head>
      <body>
        <div id="app">Server content</div>
        <script src="/client/main.js"></script>
      </body>
    </html>
  `;
  res.send(html);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Working Code Examples

Complete Universal Application:

// src/shared/utils.js
class UniversalUtils {
  static isBrowser() {
    return typeof window !== 'undefined' && typeof document !== 'undefined';
  }

  static isServer() {
    return !this.isBrowser();
  }

  static getStorage(key) {
    if (this.isBrowser()) {
      try {
        return localStorage.getItem(key);
      } catch (error) {
        console.warn('localStorage not available:', error);
        return null;
      }
    }
    return null;
  }

  static setStorage(key, value) {
    if (this.isBrowser()) {
      try {
        localStorage.setItem(key, value);
        return true;
      } catch (error) {
        console.warn('localStorage not available:', error);
        return false;
      }
    }
    return false;
  }

  static getCookie(name) {
    if (!this.isBrowser()) {
      return null;
    }
    
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
    return null;
  }

  static addClass(elementId, className) {
    if (!this.isBrowser()) {
      return false;
    }
    
    const element = document.getElementById(elementId);
    if (element) {
      element.classList.add(className);
      return true;
    }
    return false;
  }

  static setTitle(title) {
    if (this.isBrowser()) {
      document.title = title;
    }
  }

  static getURL() {
    if (this.isBrowser()) {
      return window.location.href;
    }
    return 'server://';
  }
}

// ✅ Export for both environments
if (typeof module !== 'undefined' && module.exports) {
  module.exports = UniversalUtils;
} else if (typeof window !== 'undefined') {
  window.UniversalUtils = UniversalUtils;
}

Client-Side Usage:

// src/client/app.js
import { UniversalUtils } from '../shared/utils.js';

class App {
  constructor() {
    this.data = null;
    this.init();
  }

  async init() {
    // ✅ Safe to use browser APIs when available
    if (UniversalUtils.isBrowser()) {
      UniversalUtils.setTitle('My Universal App');
      
      // ✅ Load saved data from localStorage
      const savedData = UniversalUtils.getStorage('appData');
      if (savedData) {
        this.data = JSON.parse(savedData);
      }
    }
    
    this.render();
  }

  render() {
    const html = `
      <div class="app">
        <h1>Universal App</h1>
        <p>Running in: ${UniversalUtils.isBrowser() ? 'Browser' : 'Server'}</p>
        <p>Current URL: ${UniversalUtils.getURL()}</p>
        <div id="content">${this.getContent()}</div>
      </div>
    `;
    
    if (UniversalUtils.isBrowser()) {
      const appContainer = document.getElementById('app');
      if (appContainer) {
        appContainer.innerHTML = html;
      }
    }
    
    return html;
  }

  getContent() {
    if (this.data) {
      return `<pre>${JSON.stringify(this.data, null, 2)}</pre>`;
    }
    return '<p>No data available</p>';
  }
}

// ✅ Export for server-side rendering
if (typeof module !== 'undefined' && module.exports) {
  module.exports = App;
} else if (typeof window !== 'undefined') {
  window.App = App;
}

Best Practices for Universal JavaScript

1. Always Check Environment

// ✅ Always check if browser APIs are available
if (typeof document !== 'undefined') {
  // ✅ Safe to use document
}

2. Use Feature Detection

// ✅ Use feature detection instead of environment detection
if ('localStorage' in window) {
  // ✅ localStorage is available
}

3. Provide Fallbacks

// ✅ Provide server-side fallbacks
const title = typeof document !== 'undefined' ? document.title : 'Server Rendered';

4. Separate Concerns

// ✅ Separate browser-specific and universal code
// Browser-specific code in /client/
// Universal code in /shared/
// Server-specific code in /server/

Debugging Steps

Step 1: Identify the Problem

# Check if running in Node.js or browser
console.log('Environment:', typeof window !== 'undefined' ? 'Browser' : 'Node.js');
console.log('Document available:', typeof document !== 'undefined');

Step 2: Check Error Location

// Add debugging around document usage
try {
  const element = document.getElementById('myElement');
  console.log('Element found:', element);
} catch (error) {
  console.error('Document access failed:', error);
}

Step 3: Verify Execution Context

// Check where code is being executed
console.log('Process exists:', typeof process !== 'undefined');
console.log('Window exists:', typeof window !== 'undefined');

Step 4: Test in Different Environments

// Test code in both browser and Node.js
// Use tools like JSDOM for Node.js testing

Common Mistakes to Avoid

1. Assuming Browser Environment

// ❌ Don't assume document exists
const element = document.getElementById('myElement'); // ❌ Error in Node.js

2. Not Checking for Browser APIs

// ❌ Don't use browser APIs without checking
const cookie = document.cookie; // ❌ Error in Node.js

3. Forgetting Server-Side Fallbacks

// ❌ Don't forget server-side alternatives
// Always provide fallbacks for server-side rendering

4. Mixing Browser and Server Code

// ❌ Don't mix browser and server code without proper separation
// Use conditional execution or separate modules

Performance Considerations

1. Minimize Environment Checks

// ✅ Cache environment check results
const IS_BROWSER = typeof window !== 'undefined' && typeof document !== 'undefined';

2. Optimize for Server-Side Rendering

// ✅ Optimize server-side rendering performance
// Minimize browser API usage during SSR

Security Considerations

1. Validate Environment Before Access

// ✅ Always validate environment before accessing browser APIs
if (typeof document !== 'undefined' && document.cookie) {
  // ✅ Safe access
}

2. Sanitize Browser Data

// ✅ Sanitize data coming from browser APIs
const userInput = document.getElementById('input').value;
// ✅ Always sanitize user input

Testing Universal Code

1. Unit Test Environment Detection

// Using Jest or similar testing framework
describe('Environment Detection', () => {
  test('should detect browser environment', () => {
    // Mock browser environment
    global.window = { document: {} };
    global.document = {};
    
    const utils = require('../shared/utils');
    expect(utils.isBrowser()).toBe(true);
  });
  
  test('should detect server environment', () => {
    // Clean up browser globals
    delete global.window;
    delete global.document;
    
    const utils = require('../shared/utils');
    expect(utils.isBrowser()).toBe(false);
  });
});

2. Test with JSDOM

test('should handle DOM operations safely', () => {
  const { JSDOM } = require('jsdom');
  const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
  
  global.window = dom.window;
  global.document = dom.window.document;
  
  // Test DOM operations
  const element = document.createElement('div');
  expect(element).toBeDefined();
  
  // Clean up
  delete global.window;
  delete global.document;
});

Alternative Solutions

1. Use Libraries for Universal Code

// ✅ Use libraries like isomorphic-fetch
import fetch from 'isomorphic-fetch';
// Works in both browser and Node.js

2. Use Build-Time Environment Variables

// ✅ Use build-time environment detection
if (process.env.NODE_ENV === 'browser') {
  // Browser-specific code
}

Migration Checklist

  • Identify all document usage in codebase
  • Add environment checks before document access
  • Provide server-side fallbacks for browser APIs
  • Test code in both browser and Node.js environments
  • Set up JSDOM for testing browser APIs in Node.js
  • Update documentation for team members
  • Run comprehensive tests across environments

Conclusion

The ‘document is not defined’ error is a common JavaScript issue that occurs when browser-specific code runs in non-browser environments. By following the solutions provided in this guide—whether through environment checking, universal module patterns, or proper code separation—you can ensure your JavaScript applications work seamlessly across both browser and server environments.

The key is to understand that document is a browser-specific API and to always check for its existence before using it. With proper environment detection, fallback mechanisms, and code organization, your JavaScript applications will be robust and compatible across different execution contexts.

Remember to always check for browser APIs before using them, provide server-side alternatives, separate browser and server code appropriately, and test thoroughly in all target environments to ensure your applications are truly universal and reliable.

Gautam Sharma

About Gautam Sharma

Full-stack developer and tech blogger sharing coding tutorials and best practices

Related Articles

Javascript

[FIXED]: localStorage is not defined error in JavaScript

Learn how to fix the 'localStorage is not defined' error in JavaScript applications. This comprehensive guide covers server-side rendering, Node.js, and browser compatibility.

January 2, 2026
Javascript

[SOLVED] Cannot use import statement outside a module Error in JavaScript

Learn how to fix the 'Cannot use import statement outside a module' error in JavaScript applications. This comprehensive guide covers ES6 modules, Node.js, and browser compatibility.

January 2, 2026
Javascript

Fix: CORS policy: No 'Access-Control-Allow-Origin' Error in Node & Javascript

Learn how to fix the 'CORS policy: No Access-Control-Allow-Origin' error in JavaScript and Node.js applications. This comprehensive guide covers CORS configuration, headers, and best practices.

January 2, 2026