Resolve “__dirname Is Not Defined” in Node.js

October, 20th 2024 3 min read

If you’ve switched your Node.js project to ES modules ("type": "module"), you may be surprised to see this error:

plaintext
ReferenceError: __dirname is not defined in ES module scope

This happens because unlike CommonJS, ES modules don’t include globals like __dirname and __filename.
This guide explains why this occurs, how to recreate these globals safely, and modern best practices for working with file paths in Node.js.


Why __dirname Doesn’t Exist in ES Modules

CommonJS provides:

  • __filename → full file path
  • __dirname → directory of the current module

But ES modules use URL-based resolution instead of file globals.
This design keeps Node.js consistent with browser modules.

Thus, in ESM, you must derive the path manually using:

  • import.meta.url
  • fileURLToPath()
  • path.dirname()

Fix 1 — Recreate __dirname and __filename Manually (ESM-Safe)

This is the official Node.js recommendation.

js
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

console.log(__filename);
console.log(__dirname);

Why this works:

  • import.meta.url → full URL of current ES module
  • fileURLToPath() → converts URL → filesystem path
  • dirname() → extracts directory

This behaves identically to the CommonJS globals.


Instead of repeating the boilerplate in every file, create a helper:

utils/path.js

js
import { dirname } from 'path';
import { fileURLToPath } from 'url';

export function getDirname(meta) {
  return dirname(fileURLToPath(meta.url));
}

export function getFilename(meta) {
  return fileURLToPath(meta.url);
}

Usage:

js
import { getDirname } from './utils/path.js';

const __dirname = getDirname(import.meta);
console.log(__dirname);

This keeps your code clean and portable.


Fix 3 — Switch Back to CommonJS

If you don’t need ES modules, remove "type": "module":

json
{
  "type": "commonjs"
}

Now Node.js automatically restores __dirname and __filename.


Fix 4 — Using import.meta.resolve() (Node 20+ New API)

Node 20 introduced an improved module resolution API.

Example:

js
import { resolve } from 'import-meta-resolve';
import { dirname } from 'path';

const resolved = await resolve('./file.txt', import.meta.url);
const __dirname = dirname(resolved);

This is powerful for working with relative imports inside ESM.


Fix 5 — Use process.cwd() When Appropriate

When dealing with project-level paths, you may not need __dirname at all:

js
const root = process.cwd();
console.log(root);

Use when:

  • working with config files
  • reading files relative to the project root
  • building CLIs

Avoid using it as a replacement for module-specific paths.


Fix 6 — Working With Webpack, Rollup, Vite, and Bundlers

Webpack

Disable Webpack from mocking Node globals:

js
node: {
  __dirname: false,
  __filename: false
}

Vite

Use the built-in import.meta.url:

js
new URL('./file.txt', import.meta.url).pathname;

Rollup

Define globals manually:

js
import replace from '@rollup/plugin-replace';

export default {
  plugins: [
    replace({
      preventAssignment: true,
      __dirname: JSON.stringify('/src'),
      __filename: JSON.stringify('/src/index.js'),
    }),
  ]
};

Fix 7 — Modern ESM Path Techniques (Cleaner Alternatives)

1. URL-style resolution (portable across Node + browsers)

js
const filePath = new URL('./config.json', import.meta.url);

2. Read file from ESM with fs/promises

js
import { readFile } from 'fs/promises';

const config = JSON.parse(
  await readFile(new URL('./config.json', import.meta.url), 'utf8')
);

3. Absolute path building

js
import path from 'path';

const assets = path.join(__dirname, 'assets');

Common Mistakes to Avoid

1. Using __dirname directly in ESM

js
console.log(__dirname); // Error!

2. Using path.resolve('./') expecting module directory

It resolves relative to cwd, not the file.

3. Mixing CommonJS require() with ESM

This causes resolution inconsistencies.


Quick Reference Table

APIWorks in ESM?Purpose
__dirnameCommonJS-only module dir
__filenameCommonJS-only module file
import.meta.urlURL path of module
fileURLToPath()Convert URL → file path
process.cwd()Current working directory
new URL()Browser-compatible file resolution

Summary

To fix “ReferenceError: __dirname is not defined in ES module scope”:

  1. Recreate it manually using import.meta.url & fileURLToPath().
  2. Use a utility wrapper for cleaner code.
  3. Remove "type": "module" if you prefer CommonJS.
  4. Use bundler-specific solutions for browser builds.
  5. Leverage modern ESM APIs like new URL() and import.meta.resolve().

With these tools, you can confidently manage file paths in modern Node.js and avoid breaking changes across module systems.


🟢 Updated for Node.js 22+, Webpack 5, Vite 5, and modern ESM workflows.