JavaScript Modules

Organize JavaScript code with ES6 modules using named exports, default exports, and dynamic imports.

TL;DR

  1. 01Export functions and variables using named or default export syntax.
  2. 02Import named exports using curly braces and defaults without them.
  3. 03Use dynamic import() to load modules asynchronously at runtime.

Tips

  1. 01Use named exports for multiple utilities and reserve default exports for the primary object a module provides.
  2. 02Create barrel files named index.js to simplify deeply nested import statements across your application subfolders.

Warnings

  1. 01Avoid circular module dependencies where two files import each other, as this can generate unexpected undefined bindings.
  2. 02Attempting to declare more than one default export in a single module throws a compile-time SyntaxError.

Named Exports

    export

    Exposes functions, variables, or classes from a module under specific names.

    // math.js
    export function add(a, b) { return a + b; }
    export const PI = 3.14159;
    import { ... }

    Imports specific named exports from another module file using curly brace syntax.

    import { add, PI } from "./math.js";
    console.log(add(5, PI));
    import * as namespace

    Binds all named exports to a single namespace object variable.

    import * as Math from "./math.js";
    console.log(Math.add(5, 3));
    console.log(Math.PI);
    Barrel re-export

    Re-exports items from other modules directly without importing them locally first.

    // index.js - barrel file
    export { add, subtract } from "./math.js";
    export { formatDate } from "./date.js";

Default Exports

    export default

    Exposes a single primary export value, function, or class from a module.

    // logger.js
    export default function log(msg) {
      console.log(`[LOG] ${msg}`);
    }
    Default import

    Imports a default export without using curly braces, using any local name.

    import log from "./logger.js";
    log("App started");
    Class export

    Exports an entire ES6 class definition as the default export of a module.

    // UserService.js
    export default class UserService {
      getUser(id) { return { id }; }
    }

Mixing and Re-exporting

    Mixed imports

    Imports both default and named exports within a single import statement.

    import main, { helper, VERSION } from "./utils.js";
    main();
    Default re-export

    Re-exports a default export as a named export inside barrel files.

    export { default as User } from "./User.js";
    Renamed re-export

    Renames exports during the re-export process for public API clarity.

    export { add as sum } from "./math.js";

Renaming Imports

    import as

    Renames imported values to prevent naming collisions with other local variables.

    import { add as addition } from "./math.js";
    addition(5, 3);
    Conflict resolution

    Allows importing functions with identical names by mapping them to local aliases.

    import { format as formatDate } from "./date.js";
    import { format as formatMoney } from "./currency.js";

Module Side Effects

    Side-effect import

    Imports a module purely for its side effects without binding any local variables.

    import "./polyfills.js";
    import "./analytics.js";
    Cached evaluation

    Evaluates modules only once per application lifecycle, caching subsequent imports.

    import "./init.js"; // executes
    import "./init.js"; // loads from cache
    dynamic import()

    Loads modules dynamically and asynchronously at runtime using promise logic.

    async function loadChart() {
      const { Chart } = await import("./chart.js");
      return new Chart();
    }

In Practice

FAQ