React Curriculum

Code Splitting with React.lazy & Suspense

Medium100% Free~25 mins#react#code-splitting#react-lazy#suspense#dynamic-import#web-vitals
Interview Answer (30-Second Summary)

Code splitting breaks monolithic JavaScript bundles into smaller chunks loaded on demand. React.lazy enables dynamic import() of components, while <Suspense fallback={<Spinner />}> coordinates loading boundaries during network chunk resolution.

Key Learning Objectives
✓

Implement route-based and interaction-based code splitting using React.lazy and dynamic import().

✓

Wrap lazy components inside <Suspense> boundaries with accessible fallback skeletons.

✓

Handle network chunk load failures gracefully using Error Boundaries.

✓

Implement route chunk prefetching on link hover to minimize perceived latency.

The Interview Problem & Context

Implement route-based code splitting using React.lazy, Suspense, and an Error Boundary to handle chunk loading failures.

1import React, { lazy, Suspense } from 'react';
2
3// 1. Dynamic import generates a separate JS chunk bundle
4const DashboardAnalytics = lazy(() => import('./DashboardAnalytics'));
5
6export function AnalyticsPage() {
7 return (
8 <div className="page-container">
9 <h1>Analytics</h1>
10 {/* 2. Suspense renders fallback UI while chunk downloads */}
11 <Suspense fallback={<div className="skeleton">Loading Analytics...</div>}>
12 <DashboardAnalytics />
13 </Suspense>
14 </div>
15 );
16}
Deep Architectural Explanation

Code Splitting Architecture

A single large JavaScript bundle (e.g. 2MB) hurts Initial Server Response and Largest Contentful Paint (LCP) because the browser must download, parse, and compile all code before rendering.

1. How React.lazy Works

React.lazy takes a function that returns a Promise resolving to a module with a default export:

tsx
const HeavyChart = lazy(() => import('./HeavyChart'));
  • Build tools (Webpack, Rollup, Vite) recognize import() and extract that component into a separate .js chunk file.
  • The chunk is only fetched over the network when the component is first rendered!

2. The <Suspense> Contract

Whenever a lazy component is evaluated, it throws a Promise internally.

  • <Suspense fallback={<LoadingSpinner />}> catches this thrown Promise.
  • It renders the fallback UI until the Promise resolves.
  • Once resolved, it seamlessly renders the real component.

3. Handling Chunk Load Failures (Deployment Stale Chunks)

If you deploy a new version of your app, old chunk filenames on the CDN will return 404s.
Always wrap Suspense boundaries in an Error Boundary that prompts the user to refresh the page!

Common Interview Traps & Pitfalls
✕

Declaring React.lazy inside the body of a component (it must be declared at the module top level).

✕

Forgetting the <Suspense> wrapper, causing React to crash when a lazy component mounts.

✕

Over-splitting tiny components into hundreds of micro-chunks, introducing HTTP request waterfall overhead.

Senior Interviewer Follow-Up Probes
?

How do you prefetch a lazy-loaded chunk when a user hovers over a navigation link?

?

How does SSR handle React.lazy without flash of unstyled content?

?

How does Suspense for code splitting compare to Suspense for data fetching in React 19?

Frequently Asked Interview Questions
What is code splitting and why is it important?

Code splitting divides your JavaScript bundle into smaller chunks that load on demand, reducing initial download size and improving Time to Interactive and Largest Contentful Paint.

At the top level of the module outside of component render functions, ensuring the dynamic import is only initiated once.

The chunk fetch fails and the promise rejects. If wrapped in an Error Boundary, a friendly recovery UI or reload prompt will display instead of a white screen crash.

React.lazy expects a default export. For named exports, you can chain the promise: lazy(() => import("./module").then(m => ({ default: m.NamedComponent }))).

By triggering the import() call on link hover or route focus: const loadComponent = () => import("./HeavyComponent"); onMouseEnter={loadComponent}.