No articles found
Try different keywords or browse our categories
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.
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 defineddocument is not definedUncaught ReferenceError: document is not definedCannot access 'document' before initializationdocument 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.
Related Articles
[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.
[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.
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.