Back
Web Fundamentals

Dynamic Module Loading: import() Function

Web Fundamentals
Build & Deployment: Monorepo, CI/CD, Strategies & Release SafetyState Management: Choosing the Right SolutionRedux: Predictable State Container (RTK + RTK Query)React Query (TanStack Query): Server State CachingData Fetching Patterns: REST, GraphQL, tRPC & Real-timeGraphQL Fundamentals for Frontend: Shape, Caching, and TradeoffsgRPC-Web Fundamentals: Browser Constraints and Proxy ModelCaching Strategies: Client, Server & EdgeData Normalization: Organizing State for PerformanceAPI Design Best Practices: Pagination, Errors, Versioning & Type SafetyAPI Versioning Strategies for Frontend CompatibilityPagination: Offset vs Cursor-BasedRate Limiting & API Resilience: Retries, Backoff, Jitter, IdempotencyHow Frontend Developers Can Handle Millions of API Requests Without Crashing EverythingBrowser Storage: Cookies, SessionStorage, LocalStorage, IndexedDBReal-time Communication: WebSockets, SSE & PollingWebRTC: Real-Time Communication in the BrowserCore Web Vitals: LCP, INP & CLSPerformance Optimization Trade-offsCritical Resource Prioritization: Optimize Loading OrderCode Splitting: Optimize Bundle Size with Dynamic ImportsTree Shaking: Eliminate Dead Code from Your BundleLazy Loading: Load Resources On-DemandResource Hints: Preload, Prefetch & PreconnectText Compression: Gzip and BrotliImage & Video Optimization: Modern Formats & TechniquesAdaptive Loading: Optimize for Device & NetworkList Virtualization: Render Large Lists EfficientlyWeb Workers vs Main Thread: Offloading Heavy WorkMemory Leaks in Frontend Apps: Detection & PreventionManaging Third-Party Scripts: Optimization StrategiesHow CDNs Work: Edge Delivery, Caching & PerformanceHTTP Caching Deep Dive: Cache-Control, ETag & RevalidationService Workers & Offline Strategy: Cache First, Network First & Update LifecyclePWA Fundamentals: Manifest, Installability & Offline UXCritical Rendering PathScript Loading: async vs deferEvent Loop: Understanding JavaScript Execution ModelJavaScript Module Systems: CJS vs ESM vs UMDDynamic Module Loading: import() FunctionImport on Interaction: Load When User InteractsImport on Visibility: Lazy Loading with IntersectionObserverBrowser Rendering Pipeline & Layout ThrashingRendering Strategies: CSR vs SSR vs SSG vs ISRStreaming SSR: Progressive HTML StreamingIslands Architecture: Independent Component HydrationReact Server Components: Zero-JS Server RenderingFramework Reactivity: React, Vue, Svelte & SolidHTTP/1.1 vs HTTP/2 vs HTTP/3 (QUIC) for Frontend PerformanceDNS Resolution: Path, TTL, Caching & Frontend ImpactCross-Site Scripting (XSS) AttacksCross-Site Request Forgery (CSRF) AttacksCORS Explained: Cross-Origin Resource SharingCORS Preflight in Practice: Credentials, Simple Requests & MisconfigurationsContent Security Policy (CSP)Why is HTTPS Secure? Understanding TLS/SSLAuthorization Best PracticesCookie Security & Session Hardening: SameSite, HttpOnly, Secure
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
Previous TopicJavaScript Module Systems: CJS vs ESM vs UMDNext Topic Import on Interaction: Load When User Interacts

On this page