How to Conditionally Import an ES Module in JavaScript
In modern JavaScript, static import statements are processed when the module is loaded, meaning you cannot place them inside an if block. However, there are many scenarios where you need to load a module conditionally or dynamically, such as loading a heavy library only when a user clicks a button, or choosing which module to load based on the environment.
This guide will teach you the modern, standard method for conditional loading using dynamic import(). You will learn how to use it with async/await and see how to apply it to both local files and third-party modules.
The Core Method: Dynamic import()
The import() syntax, when used as a function-like expression, is the key to dynamic loading. Unlike a static import statement, import() can be used anywhere in your code (like inside an if block or a function).
- It takes the module specifier (a path or package name) as an argument.
- It returns a
Promisethat resolves to a module namespace object.
This Promise-based behavior means it's a perfect fit for async/await.
The Modern Solution (Recommended): async/await with Dynamic import()
Using async/await is the cleanest and most readable way to handle dynamic imports.
Important: To use top-level await (outside of an async function), your file must be treated as an ES module. You can do this by:
- Setting
"type": "module"in yourpackage.jsonfor Node.js. - Using
<script type="module">in your HTML for the browser.
For example, we only want to load a module if a certain condition is met.
// Problem: How to load './utils.js' only if `condition` is true?
const condition = true;
if (condition) {
// Static import is not allowed here
// import { someUtil } from './utils.js';
}
Solution:
const condition = true;
if (condition) {
try {
// Await the promise returned by the dynamic import()
const myModule = await import('./utils.js');
// Now you can use the module's exports
myModule.someUtil();
} catch (error) {
console.error('Failed to load the module:', error);
}
}
How to Handle Named and Default Exports
The object returned by await import() is a module namespace object. It contains all the exports from that module.
Consider a module utils.js:
// In utils.js
export const namedExport = () => 'This is a named export.';
export default () => 'This is the default export.';
You can access its exports like this:
const utilsModule = await import('./utils.js');
// Access the NAMED export as a property
console.log(utilsModule.namedExport()); // Output: "This is a named export."
// Access the DEFAULT export via the `default` property
console.log(utilsModule.default()); // Output: "This is the default export."
You can also use destructuring for a cleaner syntax with named exports:
const { namedExport, default: defaultExport } = await import('./utils.js');
Practical Examples
How to Conditionally Load a Third-Party Module
This is great for "lazy loading" heavy libraries. This example loads the uuid library only when a function is called.
async function generateId() {
try {
// Dynamically import the 'uuid' package
const { v4: uuidv4 } = await import('uuid');
// Now use the imported function
return uuidv4();
} catch (error) {
console.error('Failed to load the uuid module:', error);
return null;
}
}
// The uuid library is only fetched and parsed when this button is clicked.
document.getElementById('my-button').addEventListener('click', async () => {
const newId = await generateId();
if (newId) {
console.log('Generated ID:', newId);
}
});
How to Dynamically Choose Which Module to Import
You can use a variable in your import() expression to load a module based on a condition, such as the environment.
const isProduction = process.env.NODE_ENV === 'production';
// Choose the module path based on the condition
const configPath = isProduction ? './config.prod.js' : './config.dev.js';
// Dynamically import the chosen configuration file
const configModule = await import(configPath);
const config = configModule.default;
console.log(`Loaded config for: ${config.environment}`);
Conclusion
Dynamic import() is the modern, standard, and most powerful tool for conditionally loading ES modules in JavaScript.
- It allows you to use
import()as a function inside any block of code, like anifstatement or an event handler. - It returns a
Promise, making it a perfect fit for the cleanasync/awaitsyntax. - It is the key to advanced optimization patterns like lazy loading and enables you to build more dynamic and efficient applications.