Dynamic imports

Import function

As mentioned before, it is not possible to use import inside a block statement. So, it is not possible to import conditionally or via a function like an event handler. However, there would obviously be performance benefits if we could load modules only when they are needed, rather than loading everything in advance. A recent addition to JavaScript allows you to call an import() function, taking the path to the module as an argument. This allows dynamic imports.

Function import() returns a promise, which fulfills with a module object that contains all its exports:


<script>
	import("./modules/moduleA.js").then((module) => {
	  console.log(module.someVar); // logs: "someValueA"
	});
</script>

Note that dynamic imports, such as in the example above, can be used in a <script> element that does not have a type="module" attribute. However, top level await is only available in async functions and modules.


// pi.js
const pi = Math.PI;
const tau = function() { return pi * 2;  };
export { pi, tau };

<script type="module">
  const { pi, tau } = await import("./modules/pi.js");
  console.log(pi); // logs: 3.141592653589793
  console.log(tau()); // logs: 6.283185307179586  
</script>

Now we can, for instance, include an import function in an event handler:


// greetings.js
export function welcome(user) {
  return `Hello ${user}, nice to meet you!`;
}

const sayHelloButton = document.querySelector("#sayHello");
const sayHelloOutput = document.querySelector("#sayHelloOutput");
let user = "John Doe";

sayHelloButton.addEventListener("click", () => {
  user = prompt("What's your name?");
  import("./modules/greetings.js")
    .then(module =>
      sayHelloOutput.textContent = module.welcome(user))
    .catch(error => console.error(error));
});

Top level await

As mentioned before, top level await is available within modules. We can use it for an input function:


// getPI.js
export default function() { return Math.PI };

<script type="module">
  const {default: pi} = await import('./modules/getPI.js');
  console.log(pi()); // logs: 3.141592653589793
</script>

But we can also use await for a dynamic export:


// getProducts.js
const products = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json")
  .then((response) => response.json());

export default await products;

<script type="module">
  import products from "./modules/getProducts.js";
  console.log(products[0].name); // logs: "baked beans"
</script>

In the example above await in module getProducts.js returns the value (or error) promise products resolves (or rejects) with. Any other modules which import products will wait executing until products has been downloaded and parsed, without blocking the download of other modules.