Introduction to CommonJS
By Flavio Copes
Learn how Node.js CommonJS modules use require and module.exports, how Node identifies them, and how they differ from ES modules.
CommonJS is one of the two module systems supported by Node.js. It uses require() to load a module and module.exports to expose values.
The other system is ES modules, which uses import and export. New Node.js projects can use either system, and many npm packages publish one or both formats.
Modules are very cool, because they let you encapsulate all sorts of functionality, and expose this functionality to other JavaScript files, as libraries. They let you create clearly separate and reusable snippets of functionality, each testable on its own.
How does Node.js identify a CommonJS module?
The clearest option is to use the .cjs file extension:
// uppercase.cjs
function uppercase(string) {
return string.toUpperCase()
}
module.exports = { uppercase }
For .js files, the nearest package.json controls the format:
{
"type": "commonjs"
}
Use .mjs, or "type": "module", for ES modules. Being explicit avoids surprising differences between tools.
If a .js file has no controlling type field, current Node.js versions may inspect its syntax to choose a module system. Do not depend on that fallback in a package. Set type or use an explicit extension.
How do you load a local module?
Use a relative path beginning with ./ or ../:
const { uppercase } = require('./uppercase.cjs')
console.log(uppercase('hello'))
Without ./, Node treats the value as a built-in module or package name instead of a file next to the current module.
For example, this loads an installed package:
const fastify = require('fastify')
Node’s built-in modules can use the node: prefix:
const fs = require('node:fs')
How do exports work?
Every CommonJS module starts with a module.exports object. Add properties to it to export several values:
function add(a, b) {
return a + b
}
function subtract(a, b) {
return a - b
}
module.exports = { add, subtract }
Then load them with destructuring assignment:
const { add, subtract } = require('./math.cjs')
To export one value, replace the entire object:
function createServer() {
// ...
}
module.exports = createServer
Then require() returns that function:
const createServer = require('./create-server.cjs')
exports is only a shortcut pointing at module.exports. This works:
exports.uppercase = uppercase
This does not export the function:
exports = uppercase
Assign to module.exports whenever you replace the whole exported value.
CommonJS modules are loaded synchronously
require() loads and evaluates a CommonJS module synchronously. Node caches the result after the first successful load, so later calls to the same resolved file normally receive the same exported object.
Each module has its own scope. Variables declared at the top level do not become global variables. Node also provides CommonJS-specific values such as __filename, __dirname, module, and require.
CommonJS was designed for server-side JavaScript. Browsers do not support require() directly, so browser code needs a bundler or should use native ES modules.
CommonJS and ES modules can interoperate, but the rules depend on the direction and on the Node.js version. If a package mixes formats, use explicit file extensions and follow the current Node.js CommonJS documentation and package module rules.
Related posts about js: