Default exports vs. named exports
Disclaimer: I predominantly work with React, so the opinions in this article are mainly drawn from that experience. It may or may not be less relevant in other contexts.
With ES modules, there are 2 ways to export a module: default exports, and named exports.
As a quick reminder, a default export looks something like this:
export default () => <button>...</button>
or:
const Button = () => <button>...</button>export default Button
And a named export looks something like:
export const Button = () => <button>...</button>
or:
const Button = () => <button>...</button>export { Button }
I’ve often wondered in which scenarios it makes sense to use default exports, and when I should rather opt for a named export. I have traditionally taken the approach of using a default export where possible, and supplementing with named exports as necessary, e.g. when exporting multiple items from a single module. This probably mostly stems from having encountered the import/prefer-default-export
ESLint rule which is notably included in the popular Airbnb JavaScript style guide and ESLint config.
These days, I have taken to pretty much solely using named exports. I’ve tried this approach in a couple of side projects and haven’t noticed any downsides so far.
From what I can tell, the main reason for preferring default exports is mostly to encourage the use/making of files that only export one thing (for the sake of readability). I’m not sure if there are any other real benefits, but the following points are the main reasons I’m now leaning towards the fully named exports approach:
1. Easy refactoring / renaming
This is perhaps my top reason for favouring named exports. Say you have a component called Button
and further down the line you give the button some sparkles and want to rename it to FancyButton
. Assuming you want to keep your import names in line with your export names (not 100% necessary, I suppose), with default exports you’d have to track down and manually rename every import and component instance, while with named exports you can simply rename the symbol (if your editor supports it) and all of your imports/instances will be automatically updated as well. I use VS Code, and I assume most other code editors/IDEs will have a similar feature to rename symbols. Even if your editor doesn’t support this, named exports will still make it easier to track down imports, as you have to import the exact same name as defined in the export.
2. Autocomplete & spell check
While you may have to type the extra set of curly brackets, you’ll likely make up any lost time by utilising auto-completion / intellisense. This is not only quicker, but allows you to see and choose from every export in the file as you type. It’ll also catch any spelling mistakes, which, while when using default exports wouldn’t break anything (since you can name it whatever you like), is still pretty annoying, makes your imports/exports out of sync, and can make tracking down imports harder.
3. Consistency
You’ll probably inevitably have a mix of default and named imports from third party packages, but where possible I quite like having my imports using a consistent style. Aside from making me feel all warm and fuzzy inside, it removes the guesswork in determining how you should import any given module.
4. Import / export all
In my React component files, I’ll often be exporting more than just the component itself. Quite frequently I’ll also need to export the props interface or other types, and occasionally a related helper function or constants. I also tend to have index.ts
files along with the appropriately named component file whose sole purpose is to export anything necessary from the component file, so it’s nice to be able to just export * from './my-component'
instead of re-exporting each export individually, or even just the default export separate from the named.
Wrap up
I suppose you could say that it’s mostly personal preference whether you choose to embrace the default export or stick to using named exports, however, I’ve found the points above to have real-world value (some more than others), and I don’t really see any benefit to using default exports, so why not take the small gains 💪.
I’d love to hear people’s thoughts, whether you think I’ve missed anything or if you disagree completely. Let me know if you like using default exports and if so why.