Fix '__dirname is not defined in ES module scope'
By Flavio Copes
Fix __dirname is not defined in ES module scope with import.meta.dirname in modern Node.js or a fileURLToPath fallback for older versions.
You get the “__dirname is not defined in ES module scope” error because __dirname is a CommonJS variable. In Node.js 20.11 or newer, replace it with import.meta.dirname:
console.log(import.meta.dirname)
If you must support an older Node.js version, recreate it using fileURLToPath(import.meta.url) and path.dirname().
I hit this error when I used __dirname inside an ES module.
In a Node.js script, __dirname gives you the path of the directory that contains the current JavaScript file.
Why does this error happen?
In CommonJS, Node wraps every file in a function before running it. That wrapper passes in a few variables, including __dirname and __filename.
ES modules don’t get that wrapper. So those variables don’t exist there.
You are in an ES module when your file ends in .mjs, or when your package.json contains "type": "module".
That second case is the sneaky one. You add "type": "module" to use import, and suddenly a script that worked for years throws this error.
Use import.meta.dirname in modern Node.js
Node.js added import.meta.dirname and import.meta.filename in version 20.11. They are no longer experimental in Node.js 22.16 and 24.0.
console.log(import.meta.dirname)
console.log(import.meta.filename)
This is the cleanest fix when your project runs on a recent Node.js version. These properties are available for modules loaded from the local filesystem.
You can check the exact version history in the Node.js ES modules documentation.
Support older Node.js versions
ES modules give you import.meta.url instead. It holds the full file:// URL of the current module.
You first need to import the Node.js path module and the fileURLToPath function from the url module:
import path from 'node:path'
import { fileURLToPath } from 'node:url'
Then you can replicate the functionality of __dirname in this way:
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
This also recreates __filename, the full path of the current module file.
Now you can use __dirname as usual:
console.log(__dirname)
// /Users/flavio/dev/weekly-report
fileURLToPath() does the URL-to-path conversion correctly on every platform. Don’t strip the file:// prefix by hand with string manipulation. That breaks on Windows, and on paths containing spaces or special characters.
Use a URL when you only need a nearby file
Sometimes you don’t need a directory path at all. Node.js file APIs accept file: URLs, so you can locate a file relative to the current module like this:
import { readFile } from 'node:fs/promises'
const file = new URL('./data.json', import.meta.url)
const contents = await readFile(file, 'utf8')
This works across operating systems and avoids creating a replacement __dirname variable.
Don’t confuse it with process.cwd()
One thing to watch out for: process.cwd() is not a replacement.
__dirname points to the folder where the file lives. process.cwd() points to the folder you launched the process from.
Run node scripts/build.js from your project root and they differ. Use the __dirname equivalent when you need paths relative to the file, like loading a template that sits next to your script.
Related posts about node: