Quiz

How do you import and export modules in JavaScript?

Topics
JavaScript

TL;DR

ECMAScript modules use export to define a module's public bindings and import to consume them. Named imports must match exported names; a module can also have one default export whose importer chooses the local name. Static imports are resolved before the module executes and are best for normal dependencies. Use import() when a dependency is genuinely conditional or should be loaded on demand.

Resolution depends on the host. Browsers need module scripts and URL-like specifiers (unless an import map is used); Node.js uses its ESM and package-resolution rules; bundlers may support additional aliases. An import working in one environment does not guarantee it resolves in another.

// my-module.js
export const myFunction = () => {
/* ... */
};
export default myFunction;
// app.js
import defaultFunction, { myFunction } from './my-module.js';
myFunction();
defaultFunction();

Exporting modules

Named exports

You can export multiple named items from a module. Use the export keyword before the function, variable, or class you want to export.

// myModule.js
export const myFunction = () => {
console.log('This is my function');
};
export const myVariable = 42;

Default exports

A module can have one default export. Use the export default keyword to export a single item.

// myModule.js
const myFunction = () => {
console.log('This is my function');
};
export default myFunction;

Importing modules

Importing named exports

To import named exports, use the import statement followed by curly braces containing the names of the exports.

// main.js
import { myFunction, myVariable } from './myModule';
myFunction(); // This is my function
console.log(myVariable); // 42

Importing default exports

To import a default export, use the import statement followed by a name of your choice.

// main.js
import myFunction from './myModule';
myFunction(); // This is my function

Importing all exports

You can import all named exports from a module using the * syntax and an alias.

// main.js
import * as myModule from './myModule';
myModule.myFunction(); // This is my function
console.log(myModule.myVariable); // 42

Dynamic imports

import() returns a Promise for the module namespace and can defer optional code:

async function openEditor() {
const { createEditor } = await import('./editor.js');
return createEditor(document.querySelector('#editor'));
}

Handle loading errors and avoid splitting tiny dependencies without measuring the request and execution tradeoff.

Runtime behavior and common traps

  • In a browser, load the entry with <script type="module" src="./main.js"></script>. Relative imports normally include the file extension because they are URLs.
  • Imports are live, read-only views of exported bindings. The exporting module can update a let binding; importers see the new value but cannot assign to the import.
  • A module's top-level code normally executes once per resolved module instance. Avoid surprising side effects merely from importing a utility.
  • Cyclic imports are allowed, but reading a binding before its initialization can fail. Refactor cycles that depend on execution order.
  • A default export is not inherently better than named exports. Named exports can make auto-imports and refactors clearer when a module exposes several values.
  • Do not place secrets in a browser module. Bundling or minification cannot make shipped credentials private.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise 1 of 2
Check your understanding Exercise 1 of 2

Which statements about ECMAScript module imports and exports are correct? Select all that apply.