Dynamic Module Loading: import() Function
The dynamic import() function loads JavaScript modules at runtime and returns a Promise. It is the primary mechanism for code splitting and lazy loading in modern applications. Unlike static imports, dynamic imports are evaluated at runtime, allowing you to defer non-critical code until it is actually needed.
Static imports are like pre-ordering everything before the party starts (larger initial bundle). Dynamic import() is like ordering only what guests actually want when they arrive — smaller initial load, but you pay a small delay when the feature is requested.
1Basic Usage
Dynamic import returns a Promise that resolves to the module namespace object. It supports await syntax and .then() chaining.
async function loadFeature() {
const module = await import('./heavy-feature.js');
module.default.init();
}
// Destructuring examples
const { default: Component } = await import('./Component.jsx');
const { util1, util2 } = await import('./utils.js');2Code Splitting & Lazy Loading
Bundlers treat import() as a split point and create separate chunks. This moves non-critical code out of the initial bundle, reducing startup time.
3Conditional & Interaction-Driven Loading
Best used for route-based loading, modals, admin panels, heavy libraries, or user-triggered features. Supports parallel loading with Promise.all().
4Advanced Patterns & Caveats
Modules are cached after first load. Fully dynamic paths (variables) are harder for bundlers to optimize. Always handle errors and show loading states.
| Property | Static import | Dynamic import() |
|---|---|---|
| Loading | Blocking (part of module graph) | Non-blocking, async |
| Behavior | Resolved at build time, always included | Resolved at runtime, on-demand |
| Use Case | Critical path code required on first paint | Optional, heavy, or late-stage features |
| Bundle Impact | Part of initial bundle | Separate chunk, loaded when needed |
Static import
Loading
Blocking (part of module graph)
Behavior
Resolved at build time, always included
Use Case
Critical path code required on first paint
Bundle Impact
Part of initial bundle
Dynamic import()
Loading
Non-blocking, async
Behavior
Resolved at runtime, on-demand
Use Case
Optional, heavy, or late-stage features
Bundle Impact
Separate chunk, loaded when needed
Common questions
- ›“What is dynamic import() and how does it differ from static import?”
- ›“How does import() enable code splitting?”
- ›“When would you use dynamic imports in a React app?”
- ›“What are the trade-offs of lazy loading with dynamic imports?”
What interviewers look for
- Understanding of code splitting and bundler behavior
- Knowledge of when to move code off the critical path
- Awareness of loading states, error handling, and caching
- Real-world patterns (routes, modals, feature flags)
Short answer (60 sec)
Dynamic import() loads a module at runtime and returns a Promise. It enables code splitting by telling bundlers to create separate chunks, allowing you to defer non-critical code until it is actually needed.
Detailed answer (senior level)
Unlike static imports that are part of the initial module graph, dynamic import() is evaluated at runtime. Bundlers automatically create separate chunks for each import() call. This is ideal for route-level splitting (React.lazy + Suspense), modals, admin panels, and heavy third-party libraries. Senior answers also cover parallel loading with Promise.all, error handling, caching behavior, and the importance of balancing bundle size with user-perceived latency.
- Using dynamic import for code required on the initial critical path
- Forgetting loading states or error handling
- Creating waterfalls instead of loading modules in parallel
- Over-splitting into too many tiny chunks
- Using fully dynamic paths that bundlers cannot optimize
- ✓import() is the foundation of code splitting and lazy loading
- ✓Use it for optional, heavy, route-based, or user-triggered features
- ✓Always pair with loading UI and proper error handling
- ✓Modules are cached after first load
- ✓Balance bundle reduction with interaction latency — measure real impact