Understand module.exports and exports in Node.js
Learn why property assignments through exports work, why reassigning exports does not replace a module, and how CommonJS consumers receive exports.
In a CommonJS module, exports initially refers to the same object as module.exports. require() returns the module's exported value, represented by module.exports. Adding a property through either initial reference works; assigning a new value to the local exports variable does not replace module.exports.
Export several properties
// math.cjs
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
// app.cjs
const math = require('./math.cjs');
console.log(math.add(2, 3)); // 5
Using module.exports.add instead of exports.add has the same effect here because both references still point to the original object.
Replace the exported value
To export one function, class, array, or a different object, assign to module.exports:
// greet.cjs
module.exports = function greet(name) {
return `Hello, ${name}`;
};
const greet = require('./greet.cjs');
console.log(greet('Ada'));
This superficially similar assignment does not work:
// broken.cjs
exports = function greet(name) {
return `Hello, ${name}`;
};
It only rebinds a local variable. With no other export assignments, consumers receive the original empty object. If you had already added properties to that original object, those properties would remain exported.
Avoid mixing references after replacement
module.exports = { enabled: true };
exports.version = '1.0'; // Writes to the old object, not the exported one.
module.exports.version = '1.0'; // Writes to the current exported object.
You can deliberately make exports refer to the replacement too, but using module.exports consistently is usually clearer. A function can also carry properties:
function run() { return 'running'; }
module.exports = run;
module.exports.stop = () => 'stopped';
The consumer can call both run() and run.stop().
Loading, caching, and ES modules
CommonJS evaluates a module when it is first required and normally caches it by resolved filename. Consumers can therefore share an exported object's mutable state. Replacing module.exports later does not update a value already returned to a caller; circular dependencies can also expose partially initialized exports. Set up the module's export contract during initialization rather than depending on a delayed reassignment.
ES module syntax uses export and import, with different binding and loading rules. exports is not a built-in variable in an ES module. Use .cjs for an explicitly CommonJS file, or check the package's type setting when using .js.
See the CommonJS module documentation.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan