Dynamic Import
Problem
Heavy dependencies load synchronously at startup even if they’re only needed for specific user actions. Date picker libraries, chart renderers, or PDF generators block initial page load despite most users never triggering the features that need them. The main bundle balloons with rarely-used utilities.
Solution
Load modules asynchronously when needed rather than bundling them upfront. This reduces initial bundle size and delays parsing costs until features are actually used.
Example
This example demonstrates loading a heavy chart library only when the user clicks to view charts, reducing the initial bundle size.
// Defer loading chart library until user requests it
button.addEventListener('click', async () => {
// Dynamic import returns a promise - module loads asynchronously
const Chart = await import('./chart.js');
// Use the loaded module after it arrives
new Chart.default(data);
});
Benefits
- Reduces initial bundle size by loading code only when needed.
- Improves time-to-interactive by deferring parsing of unused code.
- Enables loading heavy dependencies only for users who need them.
- Automatically creates code split points for bundlers.
- Can significantly improve performance on slow networks.
Tradeoffs
- Introduces loading delay when features are first accessed.
- Requires handling loading states and potential failures.
- Can complicate error handling with async module loading.
- May create jarring UX if loading indicators aren’t well designed.
- Harder to debug when dynamic imports fail to load.