There are two main module types in Javascript/Node:
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() {};
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]]