JS: Module. Dynamic Import

By Xah Lee. Date: . Last updated: .

(new in ECMAScript 2020)

What is Dynamic Import

Dynamic import is introduced in ECMAScript 2020. It let you import a module at run-time. You can import a module by condition inside a if-statement, and module name can also be a variable.

Import and export of ECMAScript 2015 are static. That means, the code runs at compile time. You cannot import at run time, such as import a lib only when needed.

Dynamic Import Syntax

import(moduleSpecifier)

xtodo work in progress

read

import(moduleSpecifier).then((x_module) => {
 // x_module is a object. keys are exported names.
 const { default: MyComponent } = x_module;
});

Full Example

// file name: lib.js
const f1 = ((x) => (x + 1));
const f2 = ((x) => (x + 2));
export { f1, f2 };
// Static import (for comparison) - must be at top level
// import { greet } from './utils.js'; // This would fail if conditional

async function loadAndUseUtils() {
  try {
    // Dynamic import with a variable path (e.g., based on condition)
    const modulePath = './utils.js'; // Could be dynamic: `./modules/${feature}.js`

    const utils = await import(modulePath); // Await the Promise

    // Access named export
    console.log(utils.greet('World')); // "Hello, World!"

    // Access default export
    const FancyGreet = utils.default;
    console.log(FancyGreet('Grok')); // "🌟 Hello, Grok! 🌟"
  } catch (error) {
    console.error('Failed to load module:', error);
  }
}

// Trigger on button click (lazy loading example)
document.getElementById('load-btn').addEventListener('click', loadAndUseUtils);

In ECMAScript 2022 , you can use await at the top level in modules (not scripts), making dynamic imports cleaner.

const utils = await import('./utils.js');
console.log(utils.greet('Top-level!'));
xtodo

ai answer

JavaScript Dynamic Imports: A Complete Tutorial

Dynamic imports (also known as import() expressions) allow you to load JavaScript modules on demand at runtime, rather than statically at the top of your file. This is one of the most powerful features for performance optimization in modern JavaScript.


Why Use Dynamic Imports?

Static import (eager):

import HeavyLibrary from './heavy-library.js'; // Loaded immediately

Dynamic import (lazy):

const button = document.getElementById('load-chart');
button.addEventListener('click', async () => {
  const { default: HeavyLibrary } = await import('./heavy-library.js');
  // Now use it
});

Basic Syntax

The import() function returns a Promise that resolves to the module namespace object.

import(moduleSpecifier)
  .then(module => {
    // module.default, module.namedExport, etc.
  })
  .catch(err => console.error(err));

With async/await (recommended):

async function loadModule() {
  try {
    const module = await import('./my-module.js');
    console.log(module);
  } catch (err) {
    console.error('Failed to load module:', err);
  }
}

Module Specifier

The argument to import() can be:

Note: You cannot use template literals with variables in some bundlers unless configured properly (most modern bundlers support it).


Working with Exports

Default Export

// math.js
export default function add(a, b) { return a + b; }
// main.js
const { default: add } = await import('./math.js');
// or
const math = await import('./math.js');
const result = math.default(2, 3);

Named Exports

// utils.js
export const PI = 3.14;
export function square(x) { return x * x; }
const utils = await import('./utils.js');
console.log(utils.PI);
console.log(utils.square(5));

You can also destructure directly:

const { PI, square } = await import('./utils.js');

Real-World Examples

1. Lazy Loading a Component (React example)

async function loadComponent() {
  const { default: HeavyComponent } = await import('./HeavyComponent.jsx');
  // Render it
}

Many frameworks (Next.js, React.lazy, Vue, Svelte) have built-in support that uses dynamic imports under the hood.

2. Feature Flags

async function initializeApp() {
  if (userHasPremium) {
    const { PremiumFeatures } = await import('./premium.js');
    PremiumFeatures.init();
  }
}

3. Route-based Code Splitting

const routes = {
  '/dashboard': () => import('./pages/Dashboard.js'),
  '/settings': () => import('./pages/Settings.js'),
};

async function navigate(path) {
  const module = await routes[path]();
  // render module.default or similar
}

Error Handling

Always wrap dynamic imports in try/catch:

async function safeImport(path) {
  try {
    return await import(path);
  } catch (error) {
    if (error.name === 'TypeError') {
      console.error('Module loading failed - network or syntax error');
    } else {
      console.error('Unexpected error:', error);
    }
    // Fallback UI or retry logic
  }
}

Advanced Topics

Importing JSON (modern browsers)

const data = await import('./config.json', { assert: { type: 'json' } });
// or in newer syntax:
const data = await import('./config.json', { with: { type: 'json' } });

Dynamic Import in CommonJS (require)

Dynamic imports work in both ESM and CommonJS modules.

Caching

Modules loaded with import() are cached just like static imports. Subsequent calls to the same specifier return the cached module instantly.

const m1 = await import('./foo.js');
const m2 = await import('./foo.js'); // Returns cached module

To force a reload (rarely needed), use cache-busting query params:

await import(`./foo.js?v=${Date.now()}`);

Browser and Environment Support

Polyfill for very old browsers (not recommended for new projects):

<script nomodule src="dynamic-import-polyfill.js"></script>

Best Practices

  1. Use async/await for readability.
  2. Keep the import call close to where the module is used.
  3. Avoid putting dynamic imports in the top level of modules unless necessary (can delay module evaluation).
  4. Preload critical chunks using <link rel="modulepreload"> for better performance.
  5. Handle loading states (spinners, skeletons) in UI.
  6. Monitor bundle size — dynamic imports create new network requests.

Common Pitfalls


Comparison: Static vs Dynamic

Feature Static import Dynamic import()
Loading time Build time / initial Runtime (on demand)
Bundle splitting Limited Excellent
Tree shaking Full Full
Conditional loading Not possible Perfect
Top-level syntax Yes Function call

Exercise: Create a simple demo with a button that loads a different theme module (light-theme.js / dark-theme.js) dynamically and applies it to the page.

Would you like me to expand any section, provide a full working demo project structure, or show integration with a specific framework (React, Vue, Svelte, Node.js)?

JavaScript. Module Import Export