241

I am trying to determine if there are any big differences between these two, other than being able to import with export default by just doing:

import myItem from 'myItem';

And using export const I can do:

import { myItem } from 'myItem';

Are there any differences and/or use cases other than this?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
ajmajmajma
  • 11,766
  • 18
  • 67
  • 124
  • 2
    Using `const` will make the identifier read-only. So in the case of primitive values, you can consider that to be immutable. Note that the value itself is not immutable, so objects, arrays, etc can be changed — just not reassigned. – spmurrayzzz Nov 09 '15 at 15:02
  • 4
    @spmurrayzzz: FWIW, import bindings are also immutable, just like `const`. – Felix Kling Nov 09 '15 at 15:07
  • thanks for the clarification @FelixKling, didn't know that – spmurrayzzz Nov 09 '15 at 15:08
  • @FelixKling: From outside, at least. They might not be constant though, the exports can be changed. – Bergi Nov 09 '15 at 15:19
  • @Bergi: right, that's why I said *import bindings* ;) – Felix Kling Nov 09 '15 at 15:45
  • @Bergi: Don't we already have a question that covers default vs named exports? Couldn't find anything, but feel like this must have been asked already. – Felix Kling Nov 09 '15 at 17:06
  • @FelixKling: Do you mean [ES6 module export options](http://stackoverflow.com/q/25494365/1048572)? – Bergi Nov 09 '15 at 17:19
  • possible duplicate of [ES6 module export options](http://stackoverflow.com/q/25494365/1048572) – Bergi Feb 09 '16 at 15:22
  • Linked - [What is “export default” in javascript?](https://stackoverflow.com/q/21117160/104380) – vsync Oct 12 '18 at 09:08

7 Answers7

378

It's a named export vs a default export. export const is a named export that exports a const declaration or declarations.

To emphasize: what matters here is the export keyword as const is used to declare a const declaration or declarations. export may also be applied to other declarations such as class or function declarations.

Default Export (export default)

You can have one default export per file. When you import you have to specify a name and import like so:

import MyDefaultExport from "./MyFileWithADefaultExport";

You can give this any name you like.

Named Export (export)

With named exports, you can have multiple named exports per file. Then import the specific exports you want surrounded in braces:

// ex. importing multiple exports:
import { MyClass, MyOtherClass } from "./MyClass";
// ex. giving a named import a different name by using "as":
import { MyClass2 as MyClass2Alias } from "./MyClass2";

// use MyClass, MyOtherClass, and MyClass2Alias here

Or it's possible to use a default along with named imports in the same statement:

import MyDefaultExport, { MyClass, MyOtherClass} from "./MyClass";

Namespace Import

It's also possible to import everything from the file on an object:

import * as MyClasses from "./MyClass";
// use MyClasses.MyClass, MyClasses.MyOtherClass and MyClasses.default here

Notes

  • The syntax favours default exports as slightly more concise because their use case is more common (See the discussion here).
  • A default export is actually a named export with the name default so you are able to import it with a named import:

    import { default as MyDefaultExport } from "./MyFileWithADefaultExport";
    
David Sherret
  • 82,097
  • 24
  • 165
  • 160
40

export default affects the syntax when importing the exported "thing", when allowing to import, whatever has been exported, by choosing the name in the import itself, no matter what was the name when it was exported, simply because it's marked as the "default".

A useful use case, which I like (and use), is allowing to export an anonymous function without explicitly having to name it, and only when that function is imported, it must be given a name:


Example:

Export 2 functions, one is default:

export function divide( x ){
    return x / 2;
}

// only one 'default' function may be exported and the rest (above) must be named
export default function( x ){  // <---- declared as a default function
    return x * x;
}

Import the above functions. Making up a name for the default one:

// The default function should be the first to import (and named whatever)
import square, {divide} from './module_1.js'; // I named the default "square" 

console.log( square(2), divide(2) ); // 4, 1

When the {} syntax is used to import a function (or variable) it means that whatever is imported was already named when exported, so one must import it by the exact same name, or else the import wouldn't work.


Erroneous Examples:

  1. The default function must be first to import

    import {divide}, square from './module_1.js
    
  2. divide_1 was not exported in module_1.js, thus nothing will be imported

    import {divide_1} from './module_1.js
    
  3. square was not exported in module_1.js, because {} tells the engine to explicitly search for named exports only.

    import {square} from './module_1.js
    
Nicholas K
  • 14,118
  • 7
  • 25
  • 49
vsync
  • 87,559
  • 45
  • 247
  • 317
  • It doesn't mean that it exports a single thing. You can have multiple named and one default in the same module. Default simply means exactly that - it's the default export if you don't specify the name when importing, i.e. `import something from` instead of `import { somethingNamed } from`. – Andris Jan 03 '19 at 14:13
  • I also learned new English word here: "Erroneous" +1 for that – Yuval Levy Nov 08 '19 at 16:21
  • This is the better answer for me with the examples and all that... thumbs up – Eric Aig Feb 21 '21 at 08:09
18

Minor note: Please consider that when you import from a default export, the naming is completely independent. This actually has an impact on refactorings.

Let's say you have a class Foo like this with a corresponding import:

export default class Foo { }

// The name 'Foo' could be anything, since it's just an
// Identifier for the default export
import Foo from './Foo'

Now if you refactor your Foo class to be Bar and also rename the file, most IDEs will NOT touch your import. So you will end up with this:

export default class Bar { }

// The name 'Foo' could be anything, since it's just an
// Identifier for the default export.
import Foo from './Bar'

Especially in TypeScript, I really appreciate named exports and the more reliable refactoring. The difference is just the lack of the default keyword and the curly braces. This btw also prevents you from making a typo in your import since you have type checking now.

export class Foo { }

//'Foo' needs to be the class name. The import will be refactored
//in case of a rename!
import { Foo } from './Foo'
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Philipp Sumi
  • 715
  • 5
  • 16
  • 2
    "*'Foo' needs to be the class name.*" - no! You can just as easily do `import { Foo as Anything } from …` as you can do `import Anything from …` with default exports. – Bergi Dec 10 '17 at 11:45
  • 1
    That you *can* rename it with an `as` is really not the point in that source comment. Thanks for the downvote ;p – Philipp Sumi Dec 10 '17 at 19:54
  • 2
    I didn't downvote, however I'm not sure whether that argument is convincing. I don't know whether I would want my IDE to rename all imports when refactoring a single module, that's exactly what modularisation is about :-) And it seems to be more of an IDE "problem" not a reason to choose the export style… – Bergi Dec 10 '17 at 20:09
  • 2
    I agree that I use named exports for the sake of developer UX here - but then, you could argue that Typescript per se is all about that. I refactor frequently, and given that I usually have one class (in my case: React Component) per file, I would absolutely want the imports to follow a renamed component in not to create a disconnect. Of course, this may or may not make sense depending on the individual developer. – Philipp Sumi Dec 10 '17 at 21:30
  • 1
    I found [an article](https://blog.neufund.org/why-we-have-banned-default-exports-and-you-should-do-the-same-d51fdc2cf2ad) which says the same thing. Maybe a reasonable position could be: we should use `export default` for exporting the main object of a project, in particular from a npm packages (it replaces a `module.exports =`). But, internally in a project, it is better to use named exports only. – Paleo May 04 '18 at 13:58
  • Thanks for including examples of the corresponding exports! – Travis Hohl Jun 30 '18 at 21:25
9

From the documentation:

Named exports are useful to export several values. During the import, one will be able to use the same name to refer to the corresponding value.

Concerning the default export, there is only a single default export per module. A default export can be a function, a class, an object or anything else. This value is to be considered as the "main" exported value since it will be the simplest to import.

Community
  • 1
  • 1
James Sumners
  • 13,485
  • 10
  • 53
  • 73
3

More important difference is: export default exports value, while export const/export var/export let exports reference(or been called live binding). Try below code in nodejs(using version 13 or above to enable es module by default):

// a.mjs

export let x = 5;
// or
// let x = 5;
// export { x }

setInterval(() => {
  x++;
}, 1000);

export default x;
// index.mjs
import y, { x } from './1.mjs';

setInterval(() => {
  console.log(y, x);
}, 1000);
# install node 13 or above
node ./index.mjs

And we should get below output:

6 5
7 5
8 5
...
...

Why we need this difference

Most probably, export default is used for compatibility of commonjs module.exports.

How to achieve this with bundler(rollup, webpack)

For above code, we use rollup to bundle.

rollup ./index.mjs --dir build 

And the build output:

// build/index.js

let x = 5;
// or
// let x = 5;
// export { x }

setInterval(() => {
  x++;
}, 1000);

var y = x;

setInterval(() => {
  console.log(y, x);
}, 1000);

Please pay attention to var y = x statement, which is the default.

webpack has similar build output. When large scale of modules are added to build, concatenateing text is not sustainable, and bundlers will use Object.defineProperty to achieve binding(or called harmony exports in webpack). Please find detail in below code:

main.js
...
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/        }
/******/    };
...
// 1.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
/* 0 */,
/* 1 */
/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "x", function() { return x; });
let x = 5;
// or
// let x = 5;
// export { x }

setInterval(() => {
  x++;
}, 1000);

/* harmony default export */ __webpack_exports__["default"] = (x);


/***/ })
]]);

Please find the difference behavior between /* harmony export (binding) */ and /* harmony default export */.

ES Module native implementation

es-modules-a-cartoon-deep-dive by Mozilla told why, what and how about es module.

1

When you put default, its called default export. You can only have one default export per file and you can import it in another file with any name you want. When you don't put default, its called named export, you have to import it in another file using the same name with curly braces inside it.

Abdullah Danyal
  • 866
  • 1
  • 7
  • 23
0

I had the problem that the browser doesn't use ES6.

I have fix it with:

 <script type="module" src="index.js"></script>

The type module tells the browser to use ES6.

export const bla = [1,2,3];

import {bla} from './example.js';

Then it should work.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Marcel Zebrowski
  • 639
  • 7
  • 12