Code

Why and How to avoid JavaScript default exports

I recently inherited a codebase that has a mixture of ‘named’ and ‘default’ exports. By mixing the two syntaxes, we broke the application. We incorrectly resolved a merge conflict which resulted in multiple hours of debugging.

Learning from this incident, we had to make a choice. We should be consistent - which method should we choose? We decided to abandon ‘default exports’. They waste time during development and lead to confusion.

Why?

The ‘default’ export should be considered a bad practice / anti-pattern [0].

By using the default export you make it a lot more difficult to work with your codebase. You can name the import differently each time. This in turn makes it more difficult and confusing for the next developer.

Writing Code gets harder

Your editor is not aware what it can import form the various files. You will always have to manually write the import statement. Which is time consuming and error prone. With named exports your IDE can make good suggestions and automatically write the import statement.

By using named exports, developers are much more aware of using more unique names. With default exports developers are not aware that they are naming exports identically. This results in multiple files exporting ‘Button’. This gets real confusing really fast. By switching over to named exports, we are now using a more descriptive name like ‘HeaderButton’, ‘InfoButton’, etc.

Tooling is significantly better

With named exports your IDE / Editor will provide you with a list of good suggestions and write the import statement for you automatically.

Debugging also becomes easier as it now will be using the direct name of the export in the stack trace.

Other optimizations for packaging your code like ‘tree-shaking’ are also made possible with named exports.

How to avoid ‘default imports’

To enforce this new rule in the codebase we use a plugin for ESLint eslint-plugin-import.

npm install eslint-plugin-import --save-dev

.eslintrc

...
  "plugins": ["eslint-plugin-import"],
  "rules": {
    "import/no-default-export": "error",
  }
...

https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md

What are the downsides not using default exports?

Nothing.

If you still run into the problem that two different files export the same variable name you can rename the variable during import:

import { Button as InfoButton } from './InfoButton';
import { Button as HeaderButton } from './HeaderButton';

More Info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

Conclusion

By using named exports you get:

  • Clearer Code
  • More unique variable Names
  • Faster Development Speed
  • More optimized production bundle

We will not immediately refactor the codebase to use named exports. For now all future code will be written with named exports - and we remove the default export when we have to touch old code.

Sources