When working with Node.js, you might encounter the error: ReferenceError: __dirname is not defined in ES module scope.This usually happens when you switch from CommonJS (require) to ES Modules (import/export). In CommonJS, __dirname and __filename are built-in globals that give you the current file’s directory and path. However, ES Modules don’t provide these by default, leading to confusion for many developers migrating their projects or using modern import syntax. ReferenceError: __dirname is not defined in ES module scope. __dirname __filename In this article, we’ll break down why this error occurs, how ES Modules differ from CommonJS, and the best practices to fix or work around the issue in Node.js. Fix ReferenceError: __dirname is Not Defined in ES Module Scope CommonJS vs ES Modules in Node.js When working with Node.js, you’ll often come across two different module systems: CommonJS (CJS) and ECMAScript Modules (ESM). Understanding their differences is key to knowing why __dirname behaves differently. __dirname CommonJS (CJS) Default module system in Node.js until recently. Uses the require() function to import modules. Automatically provides special variables like __dirname and __filename. Default module system in Node.js until recently. Node.js Uses the require() function to import modules. require() Automatically provides special variables like __dirname and __filename. __dirname __filename Example: // CommonJS example const path = require('path'); console.log(__dirname); // absolute path of the current file’s directory console.log(__filename); // absolute path of the current file // CommonJS example const path = require('path'); console.log(__dirname); // absolute path of the current file’s directory console.log(__filename); // absolute path of the current file Here, __dirname always works out of the box because CommonJS injects it into every module. __dirname ES Modules (ESM) The standardized JavaScript module system, using import and export. Works in both browsers and Node.js. Does not provide __dirname or __filename by default. The standardized JavaScript module system, using import and export. import export Works in both browsers and Node.js. Does not provide __dirname or __filename by default. __dirname __filename Example: // ES Module example import path from 'path'; console.log(__dirname); // ❌ ReferenceError: __dirname is not defined // ES Module example import path from 'path'; console.log(__dirname); // ❌ ReferenceError: __dirname is not defined Since ES Modules are designed to be universal (usable outside of Node.js), Node.js does not include Node-specific globals like __dirname. Instead, you need to rely on import.meta.url and helper functions from the url and path modules to reconstruct similar behavior. __dirname import.meta.url url path Why ReferenceError: __dirname is Not Defined in ES Module Scope __dirname If you’ve switched from CommonJS to ES Modules, the sudden absence of __dirname can feel confusing. The reason lies in the design philosophy behind ESM. __dirname ESM 1. Standardization Across Environments CommonJS was created specifically for Node.js. That’s why it includes Node-specific globals like __dirname and __filename. ES Modules, on the other hand, were designed as a standard JavaScript module system that works in both browsers and Node.js. Since browsers don’t have concepts like file system paths, Node.js doesn’t inject __dirname into ES Modules – otherwise the behavior would diverge from the standard. CommonJS was created specifically for Node.js. That’s why it includes Node-specific globals like __dirname and __filename. __dirname __filename ES Modules, on the other hand, were designed as a standard JavaScript module system that works in both browsers and Node.js. standard JavaScript module system both browsers and Node.js Since browsers don’t have concepts like file system paths, Node.js doesn’t inject __dirname into ES Modules – otherwise the behavior would diverge from the standard. __dirname 2. Different Loading Mechanism CommonJS modules are loaded and wrapped by Node.js, which is why variables like __dirname can be automatically provided. ES Modules are evaluated in a different way. Each file is treated as an independent module, and Node.js doesn’t wrap them with extra variables. Instead of giving you globals, ES Modules encourage explicit imports and usage of built-in tools like import.meta.url. CommonJS modules are loaded and wrapped by Node.js, which is why variables like __dirname can be automatically provided. __dirname ES Modules are evaluated in a different way. Each file is treated as an independent module, and Node.js doesn’t wrap them with extra variables. Instead of giving you globals, ES Modules encourage explicit imports and usage of built-in tools like import.meta.url. import.meta.url 3. Encouraging Explicit Path Handling With ESM, Node.js pushes developers to be clear and intentional about paths. With ESM, Node.js pushes developers to be clear and intentional about paths. Rather than relying on injected globals, you construct the path using: import { dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); import { dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); This makes the code more transparent and portable across different environments. This makes the code more transparent and portable across different environments. How to Fix: ReferenceError: __dirname is Not Defined in ES Module Scope __dirname Now that we understand why __dirname isn’t available in ES Modules, let’s look at the most common ways to fix it. Depending on your use case, you can choose one of the following solutions: __dirname Solution 1: Use import.meta.url + fileURLToPath (Recommended) import.meta.url + fileURLToPath The modern and most reliable approach is to reconstruct __filename and __dirname using import.meta.url. __filename __dirname import.meta.url // ES Module example import { fileURLToPath } from 'url'; import { dirname } from 'path'; // Recreate CommonJS globals const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); console.log(__dirname); // Prints current file’s directory console.log(__filename); // Prints current file path // ES Module example import { fileURLToPath } from 'url'; import { dirname } from 'path'; // Recreate CommonJS globals const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); console.log(__dirname); // Prints current file’s directory console.log(__filename); // Prints current file path This method works consistently across Node.js projects using ES Modules and is the closest equivalent to the old CommonJS globals. Solution 2: Use process.cwd() (Project Root Directory) process.cwd() If you don’t specifically need the current file’s directory and instead want to work relative to the project root, you can use process.cwd(). project root process.cwd() console.log(process.cwd()); // Prints the working directory where Node.js was started console.log(process.cwd()); // Prints the working directory where Node.js was started This is useful for reading configuration files, logs, or assets located at the root of your project. However, keep in mind that if you run your script from a different folder, the result may change. Solution 3: Stick with CommonJS If migrating to ES Modules isn’t required for your project, you can simply keep using CommonJS and continue relying on __dirname as before. __dirname Two ways to do this: Two ways to do this: 1. Rename your file from .js to .cjs. .js .cjs Or set your package.json type to CommonJS: package.json {"type": "commonjs"} {"type": "commonjs"} 2. Then, your old code will continue working: const path = require('path'); console.log(__dirname); const path = require('path'); console.log(__dirname); Summary of Fixes: For modern ESM projects → use import.meta.url + fileURLToPath. For project-root paths → use process.cwd(). For legacy projects → stay with CommonJS. For modern ESM projects → use import.meta.url + fileURLToPath. import.meta.url + fileURLToPath For project-root paths → use process.cwd(). process.cwd() For legacy projects → stay with CommonJS. Best Practices Now that you know the different fixes, it’s important to choose the right one depending on your project needs. Here are some best practices to follow: 1. Prefer import.meta.url for File-Specific Paths import.meta.url If your code needs the exact file path or directory (like serving static files, saving uploads, or working with relative imports), always use the import.meta.url + fileURLToPath solution. It’s the most future-proof and aligns with ES Module standards. If your code needs the exact file path or directory (like serving static files, saving uploads, or working with relative imports), always use the import.meta.url + fileURLToPath solution. exact file path or directory import.meta.url + fileURLToPath It’s the most future-proof and aligns with ES Module standards. 2. Use process.cwd() for Project-Wide Paths process.cwd() If you only need paths relative to the project root, process.cwd() is simpler and cleaner. Good for configuration files, logs, or assets placed in the root directory. Be careful: the result depends on where you run the node command from. If you only need paths relative to the project root, process.cwd() is simpler and cleaner. project root process.cwd() Good for configuration files, logs, or assets placed in the root directory. Be careful: the result depends on where you run the node command from. node 3. Be Consistent Across Your Project Avoid mixing CommonJS and ES Modules unless you have a strong reason. Choose one module system for your project and stick to it to reduce confusion. Avoid mixing CommonJS and ES Modules unless you have a strong reason. Choose one module system for your project and stick to it to reduce confusion. one module system 4. Only Use CommonJS if Legacy Support is Required If you’re working on an older project that already relies heavily on CommonJS, it may be easier to stick with it. For new projects, always prefer ES Modules, since they’re the future of JavaScript. If you’re working on an older project that already relies heavily on CommonJS, it may be easier to stick with it. For new projects, always prefer ES Modules, since they’re the future of JavaScript. In short: Use import.meta.url for per-file path handling. Use process.cwd() for project-root paths. Stick to one system consistently. Avoid falling back to CommonJS unless absolutely necessary. Use import.meta.url for per-file path handling. import.meta.url Use process.cwd() for project-root paths. process.cwd() Stick to one system consistently. Avoid falling back to CommonJS unless absolutely necessary. Conclusion: on ReferenceError: __dirname is not defined in ES module scope __dirname The error ReferenceError: __dirname is not defined in ES module scope ReferenceError: __dirname is not defined in ES module scope isn’t a bug – it’s a design choice. ES Modules were built to be standard across environments, which means Node.js doesn’t inject Node-specific globals like __dirname. __dirname The good news is that the fix is straightforward: Use import.meta.url with fileURLToPath when you need file-specific paths. Use process.cwd() when you only care about the project root. Or, stick with CommonJS if you’re maintaining older code. Use import.meta.url with fileURLToPath when you need file-specific paths. import.meta.url fileURLToPath Use process.cwd() when you only care about the project root. process.cwd() Or, stick with CommonJS if you’re maintaining older code. By understanding the difference between CommonJS and ES Modules, and applying the right solution for your case, you can handle file paths in Node.js confidently – without running into unexpected errors.