ยท new features

__dirname is not defined in ES module scope

Getting the __dirname is not defined error in your ES modules? Node.js now provides import.meta.dirname and import.meta.filename as native replacements, no workarounds needed.

If you've switched from CJS to ES modules in Node.js, you've likely encountered this error:

ReferenceError: __dirname is not defined in ES module scope.

This happens because __dirname and __filename are CommonJS globals that don't exist in ES modules.

There is a workaround involving fileURLToPath and dirname. But Node.js now provides native solutions with import.meta.dirname and import.meta.filename. Here's how to fix the error and use the modern approach.

Contents

Understanding the Error

In CommonJS modules, __dirname and __filename are automatically available:

app.cts
console.log(__dirname); // /Users/you/project
console.log(__filename); // /Users/you/project/app.cts

But when you use ES modules (files with .mts extension or "type": "module" in package.json), these globals don't exist:

app.mts
console.log(__dirname);
// ReferenceError: __dirname is not defined in ES module scope

This breaks code that relies on these values for file path operations, reading files relative to the current module, or resolving paths.

The Old Workaround

Before Node.js added native support, you had to recreate __dirname manually using import.meta.url:

app.mts
import { fileURLToPath } from 'url';
import { dirname } from 'path';
 
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
 
console.log(__dirname); // /Users/you/project
console.log(__filename); // /Users/you/project/app.mts

This pattern appeared in countless ES module codebases. It worked, but required extra imports and boilerplate code just to get basic path information.

The Modern Solution

Node.js v20.11.0 and v21.2.0 introduced import.meta.dirname and import.meta.filename as direct replacements:

app.mts
console.log(import.meta.dirname); // /Users/you/project
console.log(import.meta.filename); // /Users/you/project/app.mts

No imports, no helper functions, no workarounds. These properties provide the exact same values you'd get from __dirname and __filename in CommonJS.

Practical Usage

Here are common scenarios where you'd use these properties:

Reading files relative to your module:

import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
 
const configFile = join(import.meta.dirname, 'config.json');
const config = await readFile(configFile, 'utf-8');

Resolving paths for imports or resources:

import { resolve } from 'node:path';
 
const templatesDir = resolve(import.meta.dirname, '..', '/templates');
console.log(templatesDir); // "/templates"

Loading assets in a web server:

import { join } from 'node:path';
import { createReadStream } from 'node:fs';
 
const publicDir = join(import.meta.dirname, 'public');
const stream = createReadStream(join(publicDir, 'index.html'));
Back to Blog