chevron-left chevron-right

Piotr Nalepa Blog webmasterski

Is using default exports considered a bad practice? Here’s why!

This version
Published
Author
Category
JavaScript
Feedback
Write to me · Post on X

This post is 6 years old. Browsers and tools may have changed since then, so check the code against current documentation before you use it.

When writing React apps or modern JavaScript in general, we often create files with a set of reusable functions or classes. People tend to use default exports in such cases and I'm going to explain, why it's not the best idea to use them.

Section 1What is a default export?

The origin of default exports comes from a willingness to replicate the functionality of exporting modules from CommonJS spec to ES6. So what is a default export basically? Let's have a look at the following piece of code:

Example 1JavaScript
// src/components/SomeComponent.js
export default class SomeComponent {
    ...
}

So you can import it into another file like in the following example:

Example 2JavaScript
// src/App.js
import SomeComponent from './components/SomeComponent';

After importing it you can use it however you like in the code of App.js file. The important thing to note is that you don't have to use SomeComponent as a name of imported code. It can be literally anything, because there's no strict convention here.

Section 2What is a named export?

Comparing to the default exports example it looks differently. First of all there's no default keyword.

Example 3JavaScript
// src/components/SomeComponent.js
class SomeComponent {
    ...
}

export const OtherComponent = () => {
    ...
}

export { SomeComponent };

So you can import it into another file like in the following example:

Example 4JavaScript
// src/App.js
import { SomeComponent, OtherComponent } from './components/SomeComponent';

So what are the main differences between both approaches and why you might consider using default exports as a bad practice? The explanation is in the next section.

Section 3The fight

Let's compare both approaches. I'll try to explain you which of these approaches is better and why.

Section 3.1Naming convention strictness

Like I mentioned earlier, in the default export section, using default exports and importing into another file, you are not restricted to use a component name (or class name, or whatever) defined in the definition file. While importing you can change its name into anything that you find reasonable.

Example 5JavaScript
// src/components/SomeComponent.js
export default class SomeComponent { ... }

// src/App.js
import AnyComponent from './components/SomeComponent'

In case of named exports it's not so easy to use any name for imported piece of code. You are restricted to use names defined in the definition file of a piece code.

Example 6JavaScript
// src/components/SomeComponent.js
class SomeComponent { ... }

export { SomeComponent };

// src/App.js
import { SomeComponent } from './components/SomeComponent';

But what if you want to change the name of imported component? It's not complicated:

Example 7JavaScript
// src/components/SomeComponent.js
class SomeComponent { ... }

export SomeComponent;

// src/App.js
import { SomeComponent as AnyComponent } from './components/SomeComponent';

It might be useful when importing components with similar names from different sources. In the case when you're exporting many components with similar names from many different files, it might be useful to set some sort of namespace for them. Let's take a look:

Example 8JavaScript
// src/components/SomeComponent.js
class Button { ... }

export const Panel = () => {};
export const config = {};

export Button;

// src/App.js
import * as UI from './components/SomeComponent';

function App () {
    doSomething(UI.config);

    return (
        <UI.Panel>
            <h1>Title</h1>
            <UI.Button />
        </UI.Panel>
    )
}

The last way of importing components is commonly used in React environment for importing React library in TypeScript projects:

Example 9JavaScript
import * as React from 'react'

In my opinion having multiple ways of importing components versus one unreliable way of importing them is much better in terms of bulding quality apps. You can accomodate imports to your needs.

Section 3.2Error reporting

Spotting errors early is extremely important in software development. From my experience using default exports is error-prone solution because you don't know whether a specific piece of code exists in a file. When I'm using named exports my code editor can early spot errors by checking whether an imported component exists in a source file. I don't have to worry about mistyping component names. And there we are, typos, these are the most problematic errors, because sometimes they tend to be different to spot a first glance, especially when doing code reviews in apps like Github or Gitlab.

Section 3.3Long term code mainteinance

This one is pretty important when building professional apps, not just pet projects that will never get into the public. Choosing the right exporting convention, I mean using named exports over default exports increases code maintainability a lot. Let's imagine a situation when you're doing a code refactor. You're looking for occurences of a specific component in a codebase. With named exports it's pretty simple, because the imported component name usually is the same as exported one. With default exports it's not that easy, the component names might differ, sometimes a lot. It can have a different name, the name can have a typo. It's all confusing and makes code mainteinance harder.

Section 4Default exports as necessary evil sometimes

Sometimes you might be forced to use default exports anyway. Because of other tools that are used when developing React apps. Such tool's developers didn't accomodate it for using code exported using named exports convention. For instance when lazy loading React components. It's a code design decision made by React team. At the end you have to write something like the following, in order to lazy load your React component:

Example 10JavaScript
// e.g. App.js
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App {
    return (
        <div>
            <Suspense fallback={<div>Loading ...</div>}>
                <LazyComponent />
            </Suspense>
        </div>
    )
}

Section 5Summary

In projects I'm working on I'm using named exports because it makes a codebase more predictable and less error-prone. I prefer getting errors as soon as possible when exporting non-existing components and I love having classes names and component names the same as in the their source files. It helps me a lot in reading the code.

About the author

Staff Software Engineer

I've been writing this blog since 2009 and building for the web professionally for 14 years. Today I lead the frontend of the Cosmos platform at Bishop Fox, where I've been since day one of the project. Before that I worked at OLX, KUDO, Auto1 and eZ Systems. Here I write about what I've learned in practice: JavaScript, TypeScript, React, CSS and frontend architecture. I've also spoken at conferences across Poland and Europe. Got a question or an idea for a post? Let me know!