Andrew's Digital Garden

JS Modules

There are two main module types in Javascript/Node:

  • CommonJS (CJS)
  • ECMAScript Modules (ES Modules, ESM)

CommonJS

Uses the require('./file.js') syntax for importing other modules and the module.exports = syntax for exporting stuff from modules CommonJS files can use the .cjs extension to tell Node that they are in CommonJS (or .cts) CommonJS imports are synchronous CommonJS works in Node but does not work in browsers

const { foo } = require('bar') module.exports = function defaultCjsExport() {}; module.exports.namedCjsExport = function namedCjsExport() {};

ES Modules

Uses the import {stuff} from './file.js' syntax for importing and the export stuff syntax for exports ESM files can use the .mjs extension to tell Node that they are in ESM (or .mts) ESM imports are asynchronous (which also allows for top-level await) ESM is supported by all modern browsers and the latest versions of Node, but does not work at all in Node versions below 12

import { foo } from 'bar' export function namedMjsExport() {} export default function defaultMjsExport() {}

Due to ESM being more modern, much of JavaScript tooling was developed for Node in CJS. ESM can import from CJS, but has issues around named imports in particular. Note that the reverse does not work.

A key example is Jest, which only (or at least used to) works with CJS. This is a major reason for Vitest becoming more popular, as it have native ESM support.

For a more technical deep-dive, see [[20250627030616-js-execution-modules]].

https://bun.sh/blog/commonjs-is-not-going-away https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1 https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples

[[browsers]] [[buildtooling]] [[js]] [[node]]

JS Modules