DEV Community

Cover image for Use Named Exports over Default Exports in JavaScript
Braydon Coyer
Braydon Coyer

Posted on • Originally published at bundleapps.io

Use Named Exports over Default Exports in JavaScript

If you were like me when I started learning JavaScript and diving into tools like React and NodeJS, you may have been confused when importing functions or components and found yourself blindly guessing how to import them at the top of your files. Sometimes I'd throw some curly braces around the name of the function I wanted to import, while other times I'd feel lucky and forgo the curlys altogether.

More times than not, I'd wait for the compiler to let me know if it could find that function or component in the external file. If I saw a red squiggly, I'd simply try the other way of importing.

I know, I know - not really ideal.

I never really understood the difference between the two importing approaches. When should I use the curly braces and when should I just use the value of the function or component I want to import?

More importantly, though, why would someone choose one way over the other?

What I learned, after the frustration pushed me to investigate these two approaches, is that named exports — functions or components you import with curly braces — provide a handful of benefits over default exports.

Default Exports vs Named Exports

The export statement is used when creating JavaScript modules and you want to share objects, functions, or variables with other files.

What are Default Exports, anyway?

A default export can only export a single object, function, or variable and curly braces are omitted when importing in various files.

export default function greeting() {
    console.log('Hello, World!');
}

// in another file
import greeting from './greeting';

greeting(); // Output: 'Hello, World!'
Enter fullscreen mode Exit fullscreen mode

Here's something cool! Did you know that default exports DO NOT require a specific value to be used when importing?

In the example above, the default exported greeting function does not need to be imported by the same name. While this flexibility is neat, it has its flaws which I'll touch on a bit later.

Here's an example of importing a function and applying a non-related name.

export default function greeting() {
    console.log('Hello, World!');
}

// in another file
import anyNameWillWork from './greeting';

anyNameWillWork(); // Output: 'Hello, World!'
Enter fullscreen mode Exit fullscreen mode

What are Named Exports?

Named exports allow us to share multiple objects, functions or variables from a single file and were introduced with the release of ES2015.

Named exports are imported with curly braces in various files and must be imported using the name of the object, function or variable that was exported. This distinction is extremely important and is one of the benefits which I'll explain in a minute.

export function greeting() {
    console.log('Hello, World!');
}

// more than one export
export const bestMovieSeries = 'The Lord of the Rings Trilogy';

// importing in another file
import { greeting, bestMovieSeries } from './greeting';

greeting(); // Output: 'Hello, World!'

console.log(bestMovieSeries); // Output: 'The Lord of the Rings Trilogy'
Enter fullscreen mode Exit fullscreen mode

Named exports can be exported individually, as seen in the example above, or batched together and exported at the bottom of a file. I prefer to export everything at the bottom of the module.

function greeting() {
    console.log('Hello, World!');
}

const bestMovieSeries = 'The Lord of the Rings Trilogy';

export { greeting, bestMovieSeries }
Enter fullscreen mode Exit fullscreen mode

The Benefits of Named Exports

Named exports have a handful of benefits over default exported data.

Here are a few highlights.

As you can imagine, this is a huge improvement that becomes more apparent as your application gets larger over time.

Explicit over implicit

Default exports don't associate any name with the item being exported, meaning that any name can be applied during import. At first, this may sound really neat, but we've all seen the guy who imports a function with a non-descriptive name.

import x from './greeting'

// yuck
x()
Enter fullscreen mode Exit fullscreen mode

Named exports are explicit, forcing the consumer to import with the names the original author intended and removing any ambiguity.

Refactoring actually works

Because named exports require you to use the name of the object, function or variable that was exported from a module, refactoring works across the board! If you need to refactor and rename a function, the change will be effective across each file that imports it.

Codebase look-up

Similar to the benefit above, searching for specific imported functions or variables becomes trivial with named exports.

Because default exports can have any name applied to them, it's almost impossible to perform a look-up in your codebase, especially if a naming convention isn't put in place.

Better Tree Shaking

Instead of exporting a single bloated object with properties you may or may not need, named exports permit you to import individual pieces from a module, excluding unused code from the bundle during the build process.

Conclusion

My hope is that after reading this article you now know when to use curly braces when importing various things in your projects and you understand the benefits of using named exports in your modules.

There are cases where default exports make sense, but for the most part, named exports should be your default choice.

Discussion (7)

Collapse
insidewhy profile image
insidewhy • Edited on

Why do you prefer to export everything at the bottom of the module? It's more verbose, since you have to repeat every exported symbol's name, and nobody reading your code will know what is exported without having to scroll all the way to the bottom.

I think that whether something is exported or not as a very important implementation detail that should live right alongside the symbol.

Collapse
lukeshiru profile image
LUKESHIRU

Agree! Worth mentioning that if you still want to have a custom name for an import, you still can:

import { name as alias } from "./name";
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
siddharthshyniben profile image
Siddharth

How did you define a function without it's keyword?

export greeting() {
    console.log('Hello, World!');
}
Enter fullscreen mode Exit fullscreen mode

??

Collapse
codinghusi profile image
Gerrit Weiermann

Obviously a mistake. Please be nice :)

Collapse
braydoncoyer profile image
Braydon Coyer Author

Fixed. Thanks!

Collapse
braydoncoyer profile image
Braydon Coyer Author

😎 nice catch!

Collapse
iammar7 profile image
Ammar Ansari

But I believe, unnecessary named export can increase the bundle size as well.