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
Check your understanding Exercise

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