Skip to main content

JavaScript Dynamic Imports

Introduction​

When building modern web applications, optimizing performance is crucial. Loading all JavaScript code at once can lead to long initial load times, especially for large applications. This is where dynamic imports come into play.

Dynamic imports allow you to load JavaScript modules on demand, rather than loading everything upfront. This technique, often called "code splitting" or "lazy loading," helps improve application startup time by loading code only when it's needed.

In this tutorial, you'll learn how dynamic imports work, how they differ from static imports, and how to implement them in your applications.

Static vs Dynamic Imports​

Before diving into dynamic imports, let's quickly review static imports:

javascript
// Static import (always loaded when the file is executed)
import { helper } from './helper.js';

helper(); // Use the imported function

Static imports:

  • Are loaded and evaluated at the beginning of the script
  • Must be at the top level of the file (not inside functions or conditions)
  • Cannot be used in a conditional manner

Dynamic imports, on the other hand:

javascript
// Dynamic import (loaded only when this code runs)
button.addEventListener('click', async () => {
const { helper } = await import('./helper.js');
helper(); // Use the imported function
});

Dynamic imports:

  • Are loaded only when the import() function is called
  • Can be placed anywhere in your code, including inside functions or conditions
  • Return a Promise that resolves to the module namespace object

Basic Syntax of Dynamic Imports​

Dynamic imports use a function-like syntax that returns a Promise:

javascript
import('./module-path.js')
.then(module => {
// Use the module
module.someFunction();
})
.catch(error => {
// Handle errors
console.error('Failed to load module:', error);
});

Or, using async/await for cleaner code:

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

Practical Examples​

Example 1: Loading a Module on User Action​

Imagine you have a feature that's only used occasionally, like a calculator tool in a larger application. Instead of loading it immediately, you can load it when the user clicks a button:

javascript
// main.js
const calculatorButton = document.getElementById('calculator-button');

calculatorButton.addEventListener('click', async () => {
try {
// Show loading indicator
calculatorButton.textContent = 'Loading calculator...';

// Dynamically import the calculator module
const calculatorModule = await import('./calculator.js');

// Use the imported module
const calculator = calculatorModule.createCalculator();
calculator.show();

calculatorButton.textContent = 'Open Calculator';
} catch (error) {
console.error('Failed to load the calculator:', error);
calculatorButton.textContent = 'Error loading calculator';
}
});
javascript
// calculator.js
export function createCalculator() {
return {
show() {
const container = document.createElement('div');
container.innerHTML = `
<div class="calculator">
<input type="text" id="result" readonly />
<div class="buttons">
<!-- Calculator buttons would go here -->
</div>
</div>
`;
document.body.appendChild(container);
}
};
}

Example 2: Conditionally Loading Modules Based on Browser Features​

You can use dynamic imports to implement feature detection and load alternative modules:

javascript
async function loadAppropriateModule() {
if (window.WebAssembly) {
// Browser supports WebAssembly
const { initWasm } = await import('./wasm-version.js');
return initWasm();
} else {
// Fallback to JavaScript implementation
const { initJs } = await import('./js-fallback.js');
return initJs();
}
}

// Use the appropriate module
loadAppropriateModule().then(module => {
module.start();
});

Example 3: Loading Different Languages​

For multilingual applications, you can load language files dynamically:

javascript
async function loadLanguage(languageCode) {
try {
const translations = await import(`./locales/${languageCode}.js`);
updateUI(translations.default);
} catch (error) {
console.error(`Could not load language: ${languageCode}`, error);
// Fall back to default language
const defaultTranslations = await import('./locales/en.js');
updateUI(defaultTranslations.default);
}
}

// When user changes language
document.getElementById('language-selector').addEventListener('change', (event) => {
loadLanguage(event.target.value);
});

// Initialize with default language
loadLanguage('en');

Error Handling with Dynamic Imports​

When using dynamic imports, it's important to handle potential errors:

javascript
import('./module.js')
.then(module => {
// Success!
module.init();
})
.catch(error => {
// Handle different error scenarios
if (error.name === 'ChunkLoadError') {
console.error('Network error when loading the module');
showOfflineMessage();
} else {
console.error('Error loading module:', error);
showGenericErrorMessage();
}
});

Using with Modern Frameworks​

Most modern frameworks and bundlers support dynamic imports out of the box.

React Component Lazy Loading​

jsx
import React, { Suspense, lazy } from 'react';

// Lazily load the heavy component
const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
return (
<div>
<h1>My App</h1>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}

Vue Async Components​

javascript
const AsyncComponent = () => import('./AsyncComponent.vue');

new Vue({
components: {
AsyncComponent
},
template: `
<div>
<h1>My Vue App</h1>
<AsyncComponent v-if="showComponent" />
<button @click="showComponent = true">Show Component</button>
</div>
`,
data() {
return {
showComponent: false
};
}
});

Performance Benefits​

Dynamic imports offer several performance advantages:

  1. Reduced initial load time: By loading only essential code upfront
  2. Code splitting: Breaking your app into smaller chunks that can be loaded on demand
  3. Better resource utilization: Loading resources only when needed
  4. Improved caching: Smaller files can be cached more efficiently

Advanced Usage: Module Preloading​

You can combine dynamic imports with module preloading for an even better user experience:

javascript
// When user hovers over the button, preload the module
button.addEventListener('mouseenter', () => {
import('./module.js'); // Start loading but don't wait for it
});

// When user clicks, use the module (likely already loaded or loading)
button.addEventListener('click', async () => {
const module = await import('./module.js');
module.doSomething();
});

Browser Compatibility​

Dynamic imports are supported in all modern browsers, including:

  • Chrome 63+
  • Firefox 67+
  • Safari 11.1+
  • Edge 79+

For older browsers, you'll need to use a transpiler like Babel with appropriate plugins.

Summary​

JavaScript dynamic imports provide a powerful way to optimize your application's performance by loading code on demand. They allow you to:

  • Load JavaScript modules conditionally and asynchronously
  • Reduce initial load time by splitting your code into smaller chunks
  • Implement feature-based code loading
  • Improve user experience with more responsive applications

By incorporating dynamic imports into your projects, you can create faster, more efficient web applications that provide a better user experience, especially on mobile devices or slower connections.

Exercises​

  1. Create a simple application with a "Dark Mode" toggle that dynamically imports a different CSS module when switched.
  2. Build a tabbed interface where each tab's content is loaded dynamically when the tab is clicked.
  3. Implement a basic image gallery that loads high-resolution images only when the user selects a thumbnail.
  4. Create an application that dynamically loads different chart libraries depending on the type of visualization the user selects.

Additional Resources​

Happy coding!



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)