Module Exports vs. Export Default: Why Not Both? | Big Nerd Ranch (2024)

In NodeJS’s CommonJS module system, a module could only export one object: the one assigned tomodule.exports. The ES6 module system adds a new flavor of export on top of this, thedefault export.

A minimal ES6 module

A great example to illustrate this is this minimal module:

export const A = 'A'export default A

At first glance, you might think thatAhas been exported twice, so you might want to remove one of these exports.

But it wasn’t exported twice. In the ES6 module world, this rigs it up so you can both doimport A from './a'and get the default export bound toA, or doimport { A } from './a'and get the named export bound toA.

Its CommonJS equivalent

This is equivalent to the CommonJS:

const A = 'A'module.exports = { A, default: A,}

Why expose a symbol as both default and named exports?

Exposing it both ways means that if there is alsoexport const B = 'B', the module consumer can writeimport { A, B} from './a'rather than needing to doimport A, { B } from './a', because they can just grab the namedAexport directly alongside the namedBexport.

(It’s also a fun gotcha that you can’t use assignment-style destructuring syntax on the default export, so thatexport default { A, B, C }can only be destructured in a two-step ofimport Stuff from './module'; const { A, B } = Stuff. ExportingA,B, andCdirectly asexport { A, B, C }in addition to as part of the default export erases this mismatch between assignment destructuring and import syntax.)

Why use default exports at all?

  • Simplify usage:Having a default export simplifies import when the person importing the module just wants the obvious thing from there. There’s simply less syntax and typing to do.
  • Signal intent:A default export communicates the module author’s understanding about what the primary export is from their module.

Intent examples

Example: Express handler: Main and helpers

If there’s a main function and some helpers, you might export the main function as the default export, but also export all the functions so you can reuse them or test them in isolation.

For example, a module exporting an Express handler as its default might also export theparseRequestJsonandbuildResponseJsonde/serializer functions that translate from the JSON data transport format into model objects and back. This would allow directly testing these transformations, without having to work at a remove through only the Express handler.

Example: API binding: Related functions with no primary

In the case where the module groups related functions with no clear primary one, like an API module for working with a customer resource./customer, you might either omit a default export, or basically say “it’s indeed a grab bag” and export it both ways:

export const find = async (options) => { /* … */ }export const delete = async (id) => { /* … */ }export default { find, delete,

Anchored API increases context

If you similarly had APIs for working with./product, this default export approach would simplify writing code like:

import customer from './resources/customer'import product from './resources/product'export const productsForCustomer = async (customerId) => { const buyer = await customer.find(customerId) const products = await Promise.all( buyer.orders .map { order => order.productIds } .map { productId => product.find(productId) } ) return products}

Effectively, all the functions are named with the expectation that they’ll be used through that default export – they expect to be “anchored” to an identifier that provides context (“this function is finding a customer”) for their name. (This sort of design is very common in Elm, as captured in the package design guideline that“Module names should not reappear in function names”. Their reasoning behind this applies equally in JavaScript, so it’s worth reading the two paragraphs.)

Unanchored API requires aliasing and repetition

If you hadn’t provided a default export with all the functions from both resources, you’d instead have had to alias the imports:

import { find as findCustomer } from './resources/customer'import { find as findProduct } from './resources/product'export const productsForCustomer = async (customerId) => { const buyer = await findCustomer(customerId) const products = await Promise.all( buyer.orders .map { order => order.productIds } .map { productId => findProduct(productId) } ) return products}

The downsides of this are:

  • The API consumer’s aliasing workload scales linearly with the number of identifiers they want to use.
  • Different consumers may alias them to different names, which makes code written against the API less uniform (and harder to rename through search-and-replace).

The upside is:

  • It’s clear from the import list precisely which identifiers you’re importing.

This could be fixed by the module author embedding the module name in each exported identifier, at the cost of the author having to repeat the module name in every blessed export.

Summary

  • Default exports, from a CommonJS module point of view, amount to sugar for exporting and importing an identifier nameddefault.
  • There are good reasons to use both default and named exports.
  • You can make your codebase more uniform and readable by taking advantage of default exports in consuming and designing APIs.

Module Exports vs. Export Default: Why Not Both? | Big Nerd Ranch (1)

Juan Pablo Claude

ReviewerBig Nerd Ranch

During his tenure at BNR, Juan Pablo has taught bootcamps on macOS development, iOS development, Python, and Django. He has also participated in consulting projects in those areas. Juan Pablo is currently a Director of Technology focusing mainly on managing engineers and his interests include Machine Learning and Data Science.

Module Exports vs. Export Default: Why Not Both? | Big Nerd Ranch (2024)

FAQs

When to use module exports vs export default? ›

In summary, `module. exports` is associated with CommonJS modules used in Node. js, while `export default` is associated with ES6 modules used in modern JavaScript environments, including browsers and newer versions of Node.

Why avoid default exports? ›

Default exports can be imported using any name you choose. This can lead to inconsistencies when multiple engineers are working on the same codebase and using different import names.

When to use named export vs default export? ›

Named exports are useful when you have multiple entities to export, while default exports are suitable for a single, primary entity. By mastering these export techniques, you can efficiently structure your code and make it more readable and reusable.

Why would you create a default export in a module? ›

Default exports are used to export a single "main" value from a module, while named exports allow us to export multiple values with specific names. Default exports allow us to import the exported value with any name in the importing module, while named exports require us to use the exact exported names when importing.

Why do we use module exports? ›

Exports is the object that is returned to the require() call. By module. exports, we can export functions, objects, and their references from one file and can use them in other files by importing them by require() method.

Can you export and export default in the same file? ›

Named exports are useful when we want to export multiple things from a file, while default exports are useful when we want to export a single value. However, it's important to note that using both named and default exports in the same file can be considered an anti-pattern.

Why is exporting risky? ›

There are many types of export risks. While doing business internationally, companies may be affected by more extreme changes in the political environment or fluctuations in business and macroeconomic indicators than they might encounter in domestic markets. Business norms and cultures differ.

What is bad about exporting? ›

There may also be costs involved in providing after-sales service in the foreign market. And fluctuations in exchange rates can impact the cost competitiveness of exports. If the domestic currency appreciates, it can make a country's exports more expensive in foreign markets, potentially reducing demand.

Can you have multiple export defaults? ›

Every module can have two different types of export, named export and default export. You can have multiple named exports per module but only one default export.

Why should I use export default? ›

Default exports are useful when you want to export only one main thing from a file. It is like marking that one thing as the most important to share. When importing a default export in another file, you can give it any name you want during the import, and you don't need to use curly braces {} .

When to use export default? ›

In summary, export default and export with named exports are two ways to export code from a JavaScript module. export default is used to export a single value as the default export, while export with named exports is used to export multiple values as named exports.

Why do we write export default in react? ›

The default export is also used for exporting components, values, classes, etc. in React JS, but only one element can be exported to another component at a time as a default export. In other words, a file can have only one default export.

What are the different types of exporting? ›

The two main types of exporting are direct and indirect exporting. Direct exporting is a type of exporting where the company directly sells products to overseas customers. Indirect exporting is a type of exporting practiced by companies that sell products to other countries with the help of an intermediary.

What is difference between export default and export in Reactjs? ›

Use default export when you want to export only one variable or function from a file. Use named export when you want to keep the same name for your variables or functions across different files. However, it's important to note that you can use both default and named exports in the same file.

Why use export default in React? ›

People often use default exports if the file exports only one component, and use named exports if it exports multiple components and values. Regardless of which coding style you prefer, always give meaningful names to your component functions and the files that contain them.

Why use export default in vue? ›

To make our component reusable, we need to export it using the "export default" statement, as shown in the code snippet above. This allows other components in your project to import and use the component.

Can module exports be used if a module returns more than one entity True or false? ›

Answer. We can export more than one object using module.

Top Articles
Latest Posts
Article information

Author: Rev. Porsche Oberbrunner

Last Updated:

Views: 6076

Rating: 4.2 / 5 (53 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Rev. Porsche Oberbrunner

Birthday: 1994-06-25

Address: Suite 153 582 Lubowitz Walks, Port Alfredoborough, IN 72879-2838

Phone: +128413562823324

Job: IT Strategist

Hobby: Video gaming, Basketball, Web surfing, Book restoration, Jogging, Shooting, Fishing

Introduction: My name is Rev. Porsche Oberbrunner, I am a zany, graceful, talented, witty, determined, shiny, enchanting person who loves writing and wants to share my knowledge and understanding with you.