Skip to content
FLAVIO COPES
flaviocopes.com

How to enable ES Modules in Node.js

By

Learn how to enable ES Modules and the import syntax in Node.js by adding type module to your package.json or renaming files to use the .mjs extension.

~~~

To enable ES Modules in Node.js you have two options: add "type": "module" to your package.json file, or rename your files to use the .mjs extension. No flags, no tooling.

Here’s the problem this solves. Many tutorials use the import (ES Modules) syntax instead of the const .. = require() (CommonJS) syntax.

If you write import in a plain .js file of a Node.js app that’s not configured for it, you’ll get an error like this:

unexpected identifier error

unexpected identifier..

That’s because Node.js treats .js files as CommonJS by default, and CommonJS doesn’t know the import keyword.

Option 1: set “type”: “module”

Add this line to your package.json:

{
  "type": "module"
}

From now on, Node.js treats every .js file in the project as an ES module. You can write:

import { readFile } from 'node:fs/promises'

const data = await readFile('notes.txt', 'utf8')
console.log(data)

Notice the top-level await. That’s an ES modules feature, you get it for free.

Be careful: require() stops working in those files. If one file still needs CommonJS, rename it to .cjs and Node.js will treat it as CommonJS regardless of the package.json setting.

Option 2: use the .mjs extension

If you don’t want to change the whole project, rename a single file from app.js to app.mjs.

Node.js always treats .mjs files as ES modules, no package.json change needed. Then run it as usual:

node app.mjs

Watch out for the differences

ES modules in Node.js are stricter than what you might be used to from bundlers.

Relative imports need the full file extension. This works:

import { sum } from './math.js'

But import { sum } from './math' fails with ERR_MODULE_NOT_FOUND. Adding the .js extension fixes it.

Also, __dirname and __filename don’t exist in ES modules. In recent Node.js versions you can use import.meta.dirname and import.meta.filename instead.

A historical note

When I first wrote this post, ES modules support in Node.js was experimental, and you had to run node --experimental-modules app.js. That flag hasn’t been needed for years now.

Same for Babel and the esm npm module: old tutorials recommended them to get import working, but on any modern Node.js version you don’t need them for this.

Tagged: Node.js · All topics
~~~

Related posts about node: