Node.js - difference between module import systems (CommonJS, ESM, UMD etc)

Published on
Table of Contents

CJS - CommonJS

// importing: 
const someImportedModule = require('./your-file.js'); 
const anotherImportedModule = require('some-package'); 

// exporting:
module.exports = function yourExportedFunction() {
  // ...
}
  • This is the standard and common way to import in Node.
  • You can export anything (function, string, number, object etc)
  • Imports via CJS are a copy of the exported object.
  • does not work in browsers

ESM imports (ES Modules)

// importing
import React from 'react';
import {useRef} from 'react';

// exporting
export default function sayHi() {
    return 'hi';
}

export const sayBye = () => {
    return 'bye';
}
  • This is the standard you probably know about if you use React.
  • Works in browsers
  • Easy to tree shake

Usage in browsers/ HTML with type="module".

<script type="module">
  import { someFunction } from 'your-module';

  someFunction();
</script>

UMD

UMD is a mix of both. It compiles to code like this:

(
    function (root, factory) {
        if (typeof define === 'function' && define.amd) {
            // AMD. 
            // Register as an anonymous module.
            define([], factory);
        } else if (typeof module === 'object' && module.exports) {
            // Node/CJS.
            module.exports = factory();
        } else {
            // Browser (root = window)
            root.returnExports = factory();
      }
    }(
        typeof self !== 'undefined' ? self : this, 
        function () {
            function yourFunctionHere() {
                console.log("It worked ok!");
            }

            // export it here:
            return {yourFunctionHere: yourFunctionHere};
         }
     )
);

Typescript

See https://www.typescriptlang.org/docs/handbook/esm-node.html for some important info if you use TS

For the last few years, Node.js has been working to support running ECMAScript modules (ESM). This has been a very difficult feature to support, since the foundation of the Node.js ecosystem is built on a different module system called CommonJS (CJS).

Interoperating between the two module systems brings large challenges, with many new features to juggle; however, support for ESM in Node.js is now implemented in Node.js, and the dust has begun to settle.

That’s why TypeScript brings two new module and moduleResolution settings: node16 and nodenext.

{
    "compilerOptions": {
        "module": "nodenext",
    }
}

other mentions/notes:

  • CJS was the standard in nodejs. then in v12.17.0, ESM support was added (without flags).
  • AMD -Asynchronous Module Definition. This is old, and not in use. You use a define() function to declare your dependancies. I have not seen it used/mentioned for a long time, and am not aware of any popular framework/library using it.
  • If you are releasing a library, I'd recommend using ESM as its the standard most of the industry is going to be heading.