JavaScript Dynamic Import

Summary: In this tutorial, you’ll learn about JavaScript dynamic import and how to use it to import modules dynamically.

Introduction to the JavaScript dynamic import

ES6 introduced the module concept that allows you to develop modular JavaScript code. Let’s start with a simple example to understand how the JavaScript dynamic import works.

Suppose we have a project with the following structure:

├── index.html
└── js
   ├── app.js
   └── greeting.jsCode language: plaintext (plaintext)

The index.html loads the app.js file from the js directory. The index.html contains a button and displays an alert if the button is clicked:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>JavaScript Dynamic Import</title>
    </head>
    <body>
        <input type="button" value="Click Me" class="btn">
        <script src="js/app.js" type="module"></script>
    </body>
</html>Code language: HTML, XML (xml)

The greeting.js module defines a sayHi() function and exports it using a named export:

export function sayHi() {
  alert('Hi');
}Code language: JavaScript (javascript)

The app.js module imports the sayHi function from the greeting.js module. It selects the button with the class .btn and calls the sayHi() function when the button is clicked:

import sayHi from './greeting.js';

const btn = document.querySelector('.btn');
btn.addEventListener('click', () => {
  sayHi();
});Code language: JavaScript (javascript)

Prior to ES2020, it was not possible to dynamically load the greeting.js module when needed.

For example, the following app.js module attempts to load the greeting.js module only when the button is clicked and causes an error:

const btn = document.querySelector('.btn');

btn.addEventListener('click', () => {
  import sayHi from './greeting.js'; // ERROR
  sayHi();
});Code language: JavaScript (javascript)

ES2020 introduced the dynamic import of the module via the function-like import() with the following syntax:

import(moduleSpecifier);Code language: JavaScript (javascript)

The import() allows you to dynamically import a module when needed.

Here is how the import() works:

  • The import() accepts a module specifier (moduleSpecifier) that has the same format as the module specifier used for the import statement. In addition, the moduleSpecifier can be an expression that evaluates to a string.
  • The import() returns a Promise that will be fulfilled once the module is loaded completely.

The following illustrates how to load the greeting.js module dynamically from the app.js module using the import() syntax:

const btn = document.querySelector('.btn');

btn.addEventListener('click', () => {
  import('./greeting.js')
    .then((greeting) => {
      greeting.sayHi();
    })
    .catch((error) => {
      console.error(error);
    });
});Code language: JavaScript (javascript)

Since the import() returns a Promise, you can use the async/await in the app.js module like this:

const btn = document.querySelector('.btn');

btn.addEventListener('click', async () => {
  try {
    let greeting = await import('./greeting.js');
    greeting.sayHi();
  } catch (error) {
    console.log(error);
  }
});
Code language: JavaScript (javascript)

Some practical use cases of JavaScript import()

The import() has the following practical use cases:

1) Loading module on demand

Some modules may not need to be available when the application starts. To improve the loading time, you can place such functionality in modules and use the import() to load them on demand like this:

function eventHandler() {
    import('./module1.js')
        .then((ns) => {
            // use the module 
            ns.func();
        })
        .catch((error) => {
            // handle error
        });
}
Code language: JavaScript (javascript)

2) Loading modules based on conditions

When placing the import() inside the conditional statement such as if-else, you can load modules based on a specific condition.

The following example loads a module that targets a specific platform:

if( isSpecificPlatform() ) {
    import('./platform.js')
    .then((ns) => {
        ns=>f();
    });
}Code language: JavaScript (javascript)

3) Computed module specifiers

The module specifier is an expression that allows you to decide which module to load at runtime.

For example, you can load a module based on the user’s locale to show the message in the user’s specific language:

let lang = `message_${getUserLocale()}.js`;

import(lang)
    .then(...);
Code language: JavaScript (javascript)

More on the import()

Using object destructuring

If a module has multiple named exports, you can use the object destructuring to receive the exporting objects. Suppose the greeting.js has two functions:

export function sayHi() {
  alert('Hi');
}

export function bye() {
  alert('Bye');
}Code language: JavaScript (javascript)

In the app.js, you can use the object destructuring as follows:

const btn = document.querySelector('.btn');

btn.addEventListener('click', async () => {
  try {
    let { sayHi, bye } = await import('./greeting.js');
    sayHi();
    bye();
  } catch (error) {
    console.log(error);
  }
});
Code language: JavaScript (javascript)

Dynamically loading multiple modules

To load multiple modules dynamically, you can use the Promise.all() method:

Promise.all([
    import(module1), 
    import(module2),
     ...])
    .then(([module1,module2,module3]) => {
        // use the modules
    });
Code language: JavaScript (javascript)

Accessing the default export

If a module has a default export, you can access it using the default keyword like this:

import(moduleSpecifier)
    .then((module) => {
        // access the default export
        console.log(module.default);
    });Code language: JavaScript (javascript)

For example, we can change the named export to default export:

export default function sayHi() {
  alert('Hi');
}Code language: JavaScript (javascript)

In the app.js file, we can import the sayHi function using a module specifier and call the sayHi() function using the default keyword:

const btn = document.querySelector('.btn');

btn.addEventListener('click', async () => {
  try {
    let greeting = await import('./greeting.js');
    greeting.default();
  } catch (error) {
    console.log(error);
  }
});Code language: JavaScript (javascript)

Summary

  • Use JavaScript import() to load a module dynamically.
  • The import() returns a Promise that will be fulfilled once the module is loaded completely.
  • Use the async / await to handle the result of the import().
  • Use the Promise.all() method to load multiple modules at once.
  • Use the object destructuring to assign variables to the exporting objects of a module.
  • Use the default keyword to access the default export.
Was this tutorial helpful ?