How to fix error "__dirname is not defined in ES module scope" in Node.js

The error message “__dirname is not defined in ES module scope” is usually encountered when using ES modules in Node.js. Unlike CommonJS modules, ES modules don’t have a __dirname variable available by default.

There are some alternative ways to get the same string that is stored in __dirname that are compatible with ES modules:

  1. Using import.meta.url and path.dirname:
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
  1. Using URL constructor:
import { URL } from 'url';

const __filename = new URL('', import.meta.url).pathname;
const __dirname = new URL('.', import.meta.url).pathname;

import.meta

import.meta is a meta-property that exposes context-specific metadata to an ES module, cannot be used outside of a module, created by the host environment, and may have different properties depending on the environment. Some common properties are:

  • import.meta.url: The full URL to the module, includes query parameters and/or hash.
  • import.meta.resolve: A function that resolves a module specifier to a URL using the current module’s URL as base.

import.meta can be useful for passing query parameters to the module, getting the current module’s file path, or accessing the module specifier resolution algorithm.

Differences between ES modules and CommonJS

If you have enabled ES modules in Node.js (via "type": "module" in package.json) or using ES modules through a module bundler like Webpack, you’ll no longer have access to following variables compared to CommonJS:

  • __dirname: The absolute path of the directory containing the current module file.
  • __filename: The filename of the current module, including its absolute path.
  • require: The function to import modules in CommonJS. In ES modules, you can use import instead. If needed, a require function can be constructed within an ES module using module.createRequire().
  • module: The object that represents the current module and has properties such as module.exports and module.id. You can use import.meta instead.
  • exports: The object that is exported by the current module, you can use export instead.
  • require.resolve: Relative resolution can be handled via new URL('./local', import.meta.url). For a complete require.resolve replacement, there is a flagged experimental import.meta.resolve API. Alternatively module.createRequire() can be used.
  • NODE_PATH: Not part of resolving import specifiers, use symlinks if this behavior is desired.
  • require.extensions: Not used by import. The expectation is that loader hooks can provide this workflow in the future.
  • require.cache: Not used by import as the ES module loader has its own separate cache.

It’s worth noting that __dirname and __filename are not global variables in Node.js, but rather they are scoped to the current module. This means that their values will be different in different modules, depending on the module’s location in the file system.