ESM: Default, Mixed, and Async
Learn about ESM default exports and imports and how to use mixed and async imports.
We'll cover the following...
Default exports and imports
One widely used feature of CommonJS is the ability to export a single unnamed entity through the assignment of module.exports
. We saw that this is very convenient as it encourages module developers to follow the single-responsibility principle and expose only one clear interface. With ESM, we can do something similar through what’s called a default export. A default export makes use of the export default
keywords, and it looks like this:
// logger.mjsexport default class Logger {constructor (name) {this.name = name}log (message) {console.log(`[${this.name}] ${message}`)} }
In this case, the name Logger
is ignored, and the entity exported is registered under the name default
. This exported name is handled in a special way, and it can be imported as follows:
// main.mjsimport MyLogger from './logger.mjs'const logger = new MyLogger('info')logger.log('Hello World')
The difference with named ESM imports is that here, we can import it and at the same time assign it a local name of our choice because the default export is considered unnamed. In this example, we can replace MyLogger
with anything else that makes sense in our context. This is very similar to what we do with CommonJS modules. Also, note that we don’t have to wrap the import name around brackets or use the as
keyword when renaming.
Internally, a default export is equivalent to a named export with default
as the name. We can easily verify this statement by running the following snippet of code:
import * as loggerModule from "./logger.mjs";console.log(loggerModule);
When executed, the previous code will print something like this:
[Module: null prototype] { default: [class Logger] }
One thing that we cannot do, though, is import the default entity explicitly. In fact, something like the following will fail:
import { default } from './logger.mjs'
The execution will fail with a SyntaxError: Unexpected reserved word
error. This happens because the default
keyword cannot be used as a variable name. It’s valid as an object attribute, so in the previous example, it’s okay to use loggerModule.default
, but we can’t have a variable named default
directly in the scope.