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.

Best practices
Illustration accompanying a Node.js development guide

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.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting