named export vs default export

January 1, 2018

written in 2018. my current preference may differ, especially in typescript projects where named exports make refactors and autocomplete easier.

ES6 provides two different ways to export a module from a file: named exports and default exports.

named exports

Named exports look like this:

export const Hello = () => {
  return <h1>Hello</h1>;
};

Here, we are exporting the Hello component by name. One advantage of this method is that we can export multiple components from a single file.

export const Hello = () => {
  return <h1>Hello</h1>;
};

export const Goodbye = () => {
  return <h2>Goodbye</h2>;
};

Correspondingly, when we want to import our components we can do so like:

import { Hello, Goodbye } from "./Greetings";

Another advantage here is that we can alias our imports like:

import * as Greetings from "./Greetings";

And then use them like so:

<Greetings.Hello />

default exports

Default exports look like:

const Hello = () => {
  return <h1>Hello</h1>;
};

export default Hello;

Unlike with named exports, with default exports we can only export one component per file. Similarly, when we import, we can only import one component (note the lack of curly braces).

import Hello from "./Hello";

Note also that unlike named exports, default exports can use any name you want. So, you could just as easily do:

import Doge from "./Hello";

I would generally avoid default-exporting an object of functions as a general pattern:

const fn1 = () => {};
const fn2 = () => {};

export default { fn1, fn2 };

Named exports are clearer for that case:

export const fn1 = () => {};
export const fn2 = () => {};
import { fn1, fn2 } from "./functions";

summary

You can use either named exports or default exports in your code. I used to prefer default exports because they avoid some naming conflicts. For example, if you had FormalGreetings and InformalGreetings in the same file, you could not do the following:

import { Hello } from "./FormalGreetings";
import { Hello } from "./InformalGreetings";

However you could do:

import FormalGreetings from "./FormalGreetings";
import InformalGreetings from "./InformalGreetings";

And use them like:

const render = () => {
  return (
    <div>
      <FormalGreetings.Hello />
      <InformalGreetings.Hello />
    </div>
  );
};

Default exports solve one naming problem but create others. Named exports tend to make imports, refactors, and tooling behavior more predictable.