Using ES modules with CommonJS modules in Node.js

Matt Lim
3 min readAug 18, 2020

--

This is not meant to be a comprehensive overview of ES modules and CommonJS modules — I do not go into depth about what modules are and how they work. Rather, it is meant to highlight the differences between ES modules and CommonJS modules and how you can use them together.

This is part two of a four part series.

  1. The three differences between require and import in Node.js
  2. Using ES modules with CommonJS modules in Node.js
  3. Using ES modules with CommonJS modules in the browser
  4. Using ES modules with CommonJS modules with webpack

Check out full code examples here: https://github.com/arcticmatt/javascript_modules/tree/master/node.

What is a module?

A module is just a single JavaScript file.

package.json “type” field

This field determines whether JavaScript files, i.e. files ending in .js, are treated as ES modules or CommonJS modules.

Example
See the last field. Valid values are:

  • "module"
  • "commonjs"

If it is not specified, it implicitly defaults to "commonjs".

{
"name": "es_module_playground",
"version": "1.0.0",
"description": "Testing ES Modules",
"main": "module1.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"type": "commonjs"
}

"commonjs" Behavior

  • .cjs files are treated as CommonJS modules
  • .js files are treated as CommonJS modules
  • .mjs files are treated as ES modules

"module" Behavior

  • .cjs files are treated as CommonJS modules
  • .js files are treated as ES modules
  • .mjs files are treated as ES modules

As you can see, the difference is how .js files are treated.

Mixing ES modules and CommonJS modules

Here are the general rules regarding what doesn’t work, accompanied by relevant errors. “Doesn’t work” means that it results in an error if you run the code with node from the command line.

Remember from above that, if "type": "module", then an ES module is any .js or .mjs file. If "type": "commonjs", then an ES module is any .mjs file.

  1. You can only use import and export in an ES module. Specifically, this means you can only use import and export in a .mjs file, or in a .js file if "type": "module".
    Cannot use import statement outside a module
    Unexpected token ‘export’
  2. You cannot use require in an ES module, you must use import.
    ReferenceError: require is not defined
  3. You cannot use require to load an ES module.
    Error [ERR_REQUIRE_ESM]: Must use import to load ES Module

So, what does work?

  1. An ES module can import exports from a CommonJS module. As far as mixing goes, that’s it.

For example, if "type": "commonjs", then this example works, i.e. node module1.mjs will run without any errors.

// module1.mjsconsole.log("require module1");import obj from "./module2.js";
console.log(`module2 = ${obj.module2}`);
// module2.jsconsole.log("require module2");
exports.module2 = "require module2";

If "type": "module", then this example works, i.e. node module1 will run without any errors.

// module1.jsconsole.log("require module1");import obj from "./module2.cjs";
console.log(`module2 = ${obj.module2}`);
// module2.cjsconsole.log("require module2");
exports.module2 = "require module2";

As a reminder, full code examples for these rules can be found here: https://github.com/arcticmatt/javascript_modules/tree/master/node.

This diagram summarizes the above rules.

Resources

--

--

Matt Lim

Software Engineer. Tweeting @pencilflip. Mediocre boulderer, amateur tennis player, terrible at Avalon. https://www.mattlim.me/