Made in Builder

Made in Builder.io

How to Build: Localization webinar on March 23rd @ 10am PST. Register Now

×

Developers

Product

Use Cases

Pricing

Developers

Resources

Company

Log in

Product

Features

Integrations

Talk to an Expert

Pricing

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

‹ Back to blog

Web Development

Node.js module.exports vs. exports: The Right Choice

February 28, 2023

Written By Vishwas Gopinath

If you've worked with Node.js, you're probably familiar with module.exports and exports keywords used to export code from one module to another. While they may seem interchangeable at first glance, there's a good reason to use one over the other.

Import export patterns

Let's start by going over a few CommonJS import export patterns in Node.js.

// math.js
const add = (a, b) => {
  return a + b;
};

const subtract = (a, b) => {
  return a - b;
};

module.exports.add = add;
module.exports.subtract = subtract;

//index.js
const math = require("./math");

console.log(math.add(2, 3)); // 5
console.log(math.subtract(2, 3)); // -1

In the first pattern, functions are attached to properties on the module.exports object. We can see the correct values logged in index.js.

// math.js
const add = (a, b) => {
  return a + b;
};

const subtract = (a, b) => {
  return a - b;
};

exports.add = add;
exports.subtract = subtract;

//index.js
const math = require("./math");

console.log(math.add(2, 3)); // 5
console.log(math.subtract(2, 3)); // -1

In the second pattern, you attach functions to properties on the exports object. The correct values are still logged in index.js.

Now, this raises the question: why use module.exports when exports seems to achieve the same result with fewer keystrokes? To answer that, let's take a look at the following two patterns.

// math.js
const add = (a, b) => {
  return a + b;
};

const subtract = (a, b) => {
  return a - b;
};

module.exports = {
  add,
  subtract,
};

//index.js
const math = require("./math");

console.log(math.add(2, 3)); // 5
console.log(math.subtract(2, 3)); // -1

In the third pattern, a new object is assigned to module.exports. The correct values are then logged in index.js.

// math.js
const add = (a, b) => {
  return a + b;
};

const subtract = (a, b) => {
  return a - b;
};

exports = {
  add,
  subtract,
};

//index.js
const math = require("./math");

console.log(math.add(2, 3)); // TypeError: math.add is not a function
console.log(math.subtract(2, 3)); // TypeError: math.subtract is not a function
console.log(math); // {}

In the fourth pattern, a new object is assigned to exports. However, this pattern does not seem to work, as math appears to be an empty object. Let's understand why.

Let's revisit how object references work in JavaScript. When you assign one object to another, both objects point to the same memory address. Modifying one will modify the other as well. Let's see this in action.

const superhero1 = {
  name: "Bruce Wayne",
};

const superhero2 = superhero1; // Create a reference to superhero1

superhero2.name = "Clark Kent"; // Modify superhero2 name

console.log(superhero1); // { name: 'Clark Kent' } superhero1 name is updated

In this example, both superhero1 and superhero2 reference the same superhero. Modifying superhero2 also modifies superhero1.

However, assigning a new object will break the reference.

const superhero1 = {
  name: "Bruce Wayne",
};

let superhero2 = superhero1; // Create a reference to superhero 1

superhero2 = { name: "Clark Kent" }; // Assignment breaks the reference

superhero2.name = "Barry Allen"; // Only superhero2 is modified

console.log(superhero1); // { name: 'Bruce Wayne' } superhero1 is unaffected

In this case, the assignment breaks the reference and modifying superhero2 no longer affects superHero1.

Now that we understand how objects work in JavaScript, let's relate it to module.exports and exports. In Node.js, module is a plain JavaScript object with an exports property. exports is a plain JavaScript variable that happens to be set to module.exports. When you require a module in another file, the code within that module is executed, and only module.exports is returned.

var module = { exports: {} };

var exports = module.exports;

// Assigning exports to module.exports via object reference...

exports.add = add; // ...results in module.exports.add = add
exports.subtract = subtract; // ...results in module.exports.subtract = subtract

// The module.exports object contains both add and subtract properties

return module.exports;

However, if you assign a new object to exports, the reference is broken and updating exports no longer updates module.exports.

var module = { exports: {} };

var exports = module.exports;

// Below assignment breaks the object reference

exports = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
};

return module.exports; // module.exports = {}

If you try to access add or subtract on the exported object, an error is thrown because module.exports is empty. So, while module.exports and exports may seem interchangeable in the first import export pattern, they are not the same.

When should you choose exports over module.exports? The short answer is that you probably shouldn't. While exports may be shorter and seem more convenient, the confusion it can cause is not worth it. Remember that exports is just a reference to module.exports, and assigning a new object to exports breaks that reference.

Visually build with your components

Builder.io is a headless CMS that lets you drag and drop with your components right within your existing site.

// Dynamically render your components
export function MyPage({ json }) {
  return <BuilderComponent content={json} />
}

registerComponents([MyHero, MyProducts])

Share

Twitter
LinkedIn
Facebook

Builder.io is a headless CMS that lets you drag and drop with your components.

Learn more

Like our content?

Sign up for our newsletter


Continue Reading
Web Development12 MIN
Signals vs. Observables, what's all the fuss about?
WRITTEN BYMiško Hevery
March 20, 2023
software engineering5 MIN
Using Feature Flags for API Version Upgrades
WRITTEN BYShyam Seshadri
March 20, 2023
performance optimization5 MIN
Inspect Source Code Performance with Deoptigate
WRITTEN BYMiško Hevery
March 14, 2023