DEV Community

Cover image for Why to Avoid Default Exports in JavaScript Modules
Zachary Lee
Zachary Lee

Posted on • Edited on • Originally published at webdeveloper.beehiiv.com

Why to Avoid Default Exports in JavaScript Modules

Latest updates in my newsletter.

JavaScript modules are a way to organize code into reusable components that can be shared across different files and projects. Modules can be imported and exported using two different methods: default exports and named exports. In this article, we’ll take a closer look at default exports and why you should avoid using them in your JavaScript modules.

What are Default Exports in JavaScript Modules?

Default exports allow you to export a single value from a module that can be imported with any name.

This value is the default export for the module and can be any type, such as a function, object, or primitive value. Here’s an example of a module that exports a default function:

// math.js
export default function subtract(a, b) {
 return a - b;
}
Enter fullscreen mode Exit fullscreen mode

In this example, the subtract function is the default export for the math module. It can be imported with any name in another file, like this:

// app.js
import add from './math.js';
const result = add(2, 2); // returns 0
Enter fullscreen mode Exit fullscreen mode

Notice that we imported the subtract function as multiply. This is possible because default exports can be imported with any name. This can lead to confusion and make code harder to maintain as a codebase grows.

The Problem with Default Exports

Default Exports are Not Discoverable

One of the main problems with default exports is that they are not discoverable.

When you import a module that has only named exports, your IDE can show you a list of available exports that you can use in your code. However, this is not possible with default exports because they are not named. This means that developers may not know that a module has a default export, or they may not know what it is called. This can lead to confusion and make it harder to use the module.

img

VSCode shows the available list

Default Exports are Refactoring Unfriendly

Another problem with default exports is that they are refactoring unfriendly.

When you rename a named export in a module, any usage of that export can be automatically renamed as well, provided that you are using an IDE that supports this feature. However, this is not possible with default exports because they can be imported with any name. This means that if you rename a default export, you must manually update any usage of it in your code.

img

Named exports can be automatically renamed

Default Exports are Naming Nightmares

Default exports can also lead to naming nightmares. Because they can be imported with any name, different developers may use different names for the same default export. This can lead to inconsistency and make code harder to maintain as a codebase grows. In contrast, named exports provide a clear and consistent naming convention that makes code easier to understand and maintain.

Alternatives to Default Exports

Named Exports

Named exports are a good alternative to default exports. With named exports, you can export multiple values from a module and give each one a clear and consistent name. This makes it easier for other developers to understand and use your code.

Here’s an example of a module that exports two named functions:

// math.js
export function add(a, b) {
 return a + b;
}

export function subtract(a, b) {
 return a - b;
}
Enter fullscreen mode Exit fullscreen mode

In this example, we exported two named functions, add and subtract. These functions can be imported in another file like this:

// app.js
import { add, subtract } from './math';

const sum = add(2, 2); // returns 4
const difference = subtract(2, 2); // returns 0
Enter fullscreen mode Exit fullscreen mode

Notice that we imported the functions using their names, which makes the code easier to understand and maintain.

Default Export Wrappers

Another alternative to default exports is to use a default export wrapper. This involves creating a separate module that exports a default function that simply imports and re-exports the desired value from the original module.

// math.js
export function add(a, b) {
 return a + b;
}
export function subtract(a, b) {
 return a - b;
}
// math-wrapper.js

// Choose what you want to export
export { add, subtract } from './math';

// OR export as a whole
export * as default from './math';
Enter fullscreen mode Exit fullscreen mode

This wrapper is another layer of abstraction where you can select the modules you want to export from the associated folder or export all the contents of a file as default. This way, you can use them in app.js like this:

img

This also maintains clear and consistent naming, avoiding the pitfalls of default exports.

Necessity of Default Exports

There are no absolutes in everything, and default exports are necessary in some cases. For example, in common component encapsulation:

$ ls ./components                   
Row.jsx     Form            Upload.jsx
Select      Alert.jsx       Skeleton
...
Enter fullscreen mode Exit fullscreen mode

Here, a folder or file represents a component, and in this case, you may have to use default exported components.

The recommendation here is to name the default exported component consistent with the file name and the component name within the file. This minimizes misunderstandings and errors and allows you to change names all at once in the IDE.

// app.js
import Row from './Row.jsx';

import Select from "./Select"
Enter fullscreen mode Exit fullscreen mode

In addition, it is more recommended to use index.js in the components folder to aggregate these components and use named exports:

// components/index.js
export { default as Row } from './Row.jsx';
export { default as Select } from './Select';
// app.js
import { Row, Select } from "./components";
Enter fullscreen mode Exit fullscreen mode

Other Best Practices

In addition to the alternatives we discussed, there are some other best practices you can follow to improve the maintainability of your code:

  • Use consistent naming conventions for your exports. This will make it easier for other developers to understand and use your code.
  • Avoid exporting too many values from a single module. This can make your code harder to understand and maintain.

Conclusion

While default exports can be useful in certain situations, they should be used with caution and only when necessary. Again, this is just general advice. There is no one-size-fits-all solution for every project. Ultimately, it’s up to you to choose the most suitable method.

Top comments (1)

Collapse
 
wizard798 profile image
Wizard

Wow, great article. Thanks for this