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.
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.
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
.
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
Here, __dirname
always works out of the box because CommonJS injects it into every module.
ES Modules (ESM)
- The standardized JavaScript module system, using
import
andexport
. - Works in both browsers and Node.js.
- Does not provide
__dirname
or__filename
by default.
Example:
// 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.
Why ReferenceError: __dirname
is Not Defined in ES Module Scope
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.
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.
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
.
3. Encouraging Explicit Path Handling
- 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);
- This makes the code more transparent and portable across different environments.
How to Fix: ReferenceError: __dirname
is Not Defined in ES Module Scope
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:
Solution 1: Use import.meta.url + fileURLToPath
(Recommended)
The modern and most reliable approach is to reconstruct __filename
and __dirname
using 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
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)
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()
.
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.
Two ways to do this:
1. Rename your file from .js
to .cjs
.
Or set your package.json
type to CommonJS:
{"type": "commonjs"}
2. Then, your old code will continue working:
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.
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
- 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.
2. Use process.cwd()
for Project-Wide Paths
- 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.
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.
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.
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.
Conclusion: on ReferenceError: __dirname
is not defined in ES module scope
The error
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
.
The good news is that the fix is straightforward:
- Use
import.meta.url
withfileURLToPath
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.
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.