Using ES modules with CommonJS modules with webpack

Matt Lim
4 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/webpack_demo.

Webpack modules

Webpack treats modules differently than Node.js or the browser. In short, with webpack, modules are resolved when code is bundled, not when it is run. This is explained in more detail below.

When you run a script that has imports with Node.js, e.g. node script_with_imports.js, the imports will be pulled in at runtime.

When the browser runs an ES module that contains imports, something similar happens — the imports will be pulled in when the script is run. Unlike with Node.js, a network request is required in order to resolve modules, instead of simply accessing the file system. In order to see this, try the following with this example code:

javascript_modules/browser $ python3 -m http.server 1234

Then, open up your browser’s developer tools and go to http://localhost:1234/index.html. Here’s what the network tab looks like:

You can see that index.js is fetched and then es_module.js is fetched, since index.js imports it.

What about webpack? Well, webpack is a bundler — in simple terms, this means that it takes many JavaScript files as input and outputs a single JavaScript file. Basically, if webpack were a JavaScript function, it would look like webpack(javascriptFiles: string[]): string. This means that modules get resolved before any code gets run. For example, let’s say we have some code like this:

// index.jsimport { myEsModuleExportedFunction } from "./es_module.js";
const { myCommonJsModuleExportedFunction } = require("./commonjs_module.js");
// do some stuff...

If you bundle this code by running npx webpack, the resulting main.js file will not use import or require, since everything is in a single file.

Mixing module methods

Since webpack handles “module methods” itself (e.g. import, export, require, exports, etc.), it can be more flexible about what it allows than Node.js or browser engines. In general, with webpack, pretty much anything goes. Webpack supports:

  • ES module statements (import and export)
  • CommonJS require statements
  • AMD define and require statements
  • and more… (see the full list here)

Furthermore, you can mix and match these. For example, as seen in the example above, you can use import and require in the same file. Remember, Node.js doesn’t allow this! Here’s the full compatibility chart (it’s pretty simple).

Not too complicated

Even though you can mix module methods, it’s recommended to stick to a consistent syntax.

A couple more things to note

There’s a lot to learn about webpack, but here are a couple more things relevant to modules.

1. As seen above, you can do a lot of mixing and matching of module methods in webpack. However, one thing that is not allowed is mixing the ESM export statement and CommonJS exports. For example:

console.log("export_both");exports.exportBothCommonJs = function () {
return "lion";
};
// If you want to use exports, this needs to be commented out.
// export function exportBothEs() {
// return "tiger";
// }

If both exports are present, you will get this error in your browser: Uncaught ReferenceError: exports is not defined. Feel free to try this out for yourself using the example code.

2. One other thing you should know is that the code webpack generates for import statements and require statements is different. In short, it will be similar to the difference noted in #2 here. You can also try this out for yourself by cloning the example repository, running npx webpack, and looking at the resulting bundled JavaScript file when index.js uses both import and require.

Resources

--

--

Matt Lim

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