Back
Web Fundamentals

Dynamic Module Loading: import() Function

On this page

mediumRendering & Browser Architecture

Dynamic Module Loading: import() Function

TL;DRimport() loads modules at runtime as a Promise → enables code splitting and lazy loading
High Signal
Google
Meta
Agoda
Meesho
30-Second Answerstart every interview with this

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.

User Trigger / Condition
import('./heavy-feature.js')
Fetch Chunk
Execute Module
Feature Available

1Basic Usage

Dynamic import returns a Promise that resolves to the module namespace object. It supports await syntax and .then() chaining.

dynamic-import.jsjs
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.

PropertyStatic importDynamic import()
LoadingBlocking (part of module graph)Non-blocking, async
BehaviorResolved at build time, always includedResolved at runtime, on-demand
Use CaseCritical path code required on first paintOptional, heavy, or late-stage features
Bundle ImpactPart of initial bundleSeparate 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
Key Takeaways
  • ✓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

Related web fundamentals topics

Continue with closely related browser and frontend engineering concepts.

JavaScript Module Systems: CJS vs ESM vs UMDImport on Interaction: Load When User InteractsEvent Loop: Understanding JavaScript Execution ModelImport on Visibility: Lazy Loading with IntersectionObserverScript Loading: async vs deferBrowser Rendering Pipeline & Layout Thrashing
Previous TopicJavaScript Module Systems: CJS vs ESM vs UMDNext Topic Import on Interaction: Load When User Interacts