CommonJS (CJS) is Node's original module system. ES modules (ESM) are the official JavaScript standard. The main difference: CommonJS loads at runtime and can be anywhere, while ES module imports are static and can be analyzed before the code runs.
CommonJS:
- It uses
require()andmodule.exports. - Loading is synchronous and happens at runtime. So
requirecan appear anywhere, even inside anif. - You get a copy of the exported value at the moment of the call. For objects, the copy is a reference to the same object.
ES modules:
- They use
importandexport. - Static
importdeclarations must be at the top level. So tools can read the dependency graph before running the code. This enables tree shaking (removing unused code). - Loading is asynchronous in browsers.
- Imports are live read-only bindings. "Live" means you always see the exporter's current value. "Read-only" means the importer cannot assign to it.
- Modules are always strict, top-level
thisisundefined, and top-levelawaitis allowed. import()loads a module dynamically and returns a promise.
In Node: a file is ESM if it ends in .mjs, or if package.json has "type": "module". ESM can import CommonJS. Recent Node versions can also require() ES modules that do not use top-level await.
- CommonJS uses
requireandmodule.exports. It loads synchronously at runtime and can be dynamic. - ESM uses static
importandexport. Tools can analyze them, which enables tree shaking. - ESM imports are live bindings. CommonJS gives the value at the time of
require. - ESM is strict, supports top-level
await, and usesimport()for dynamic loading. - Node chooses the system by the
.mjsor.cjsextension, or thetypefield inpackage.json.
Read more