Lazy Loading & Suspense
React.lazy defers downloading a component's code until it's actually rendered — splitting your bundle so the first screen loads faster. Suspense shows a fallback (a spinner or skeleton) while that code is in flight. Together they're how big React apps stay fast. By the end you'll code-split a component and wrap it in a Suspense boundary.
Learn Lazy Loading & Suspense in our free React course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and a quick recall.
Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
1️⃣ React.lazy — Load on Demand
Normally every component's code is bundled and downloaded up front. React.lazy changes that: you pass it a function that calls dynamic import() , and React fetches that chunk only when the component first renders. This is code splitting — the big payoff for initial load time.
The demo models "load on demand" — nothing is fetched until you actually need it:
2️⃣ Suspense — Show a Fallback
A lazy component's code takes a moment to arrive. Suspense wraps it and shows a fallback (a spinner, skeleton, or message) during that wait, then swaps in the real component once it's ready. Without a Suspense boundary, React throws.
Your turn — model the fallback: show the placeholder while not loaded, the content when ready:
3️⃣ Placing the Boundary & Lazy Routes
One Suspense boundary can cover many lazy children — the fallback shows until they're all ready. The most common production use is lazy routes : each page is a separate chunk, downloaded only when the user navigates there.
The demo models one boundary waiting for all its lazy children before revealing them:
These lines lazy-load a component and wrap it in Suspense. Put them in a correct order:
1) When is a lazy component's code actually downloaded?
The first time it renders. Until then its chunk stays unfetched — that's what shrinks the initial bundle.
2) What happens if you render a lazy component without a Suspense boundary above it?
React throws an error — there's no fallback to show while the code loads. Wrap it in {' '} .
3) Two lazy children share one Suspense boundary; one loads in 100ms, the other in 500ms. When does the fallback disappear?
At 500ms — the boundary waits for ALL its children before revealing them.
📋 Quick Reference
Model a Suspense boundary over two children. Flip the slower one to loaded, then log what the boundary shows. Predict each output before running.
Practice quiz
What does React.lazy do?
- Caches a component's output
- Makes a component render slower
- Defers loading a component's code until it is first rendered
- Prevents a component from rendering
Answer: Defers loading a component's code until it is first rendered. React.lazy defers downloading a component's code (its chunk) until that component first renders — code splitting.
What argument does React.lazy expect?
- A function that returns a dynamic import() promise
- A component instance
- A string path
- An array of components
Answer: A function that returns a dynamic import() promise. You pass lazy a function that calls import(), e.g. lazy(() => import('./Chart')).
What is Suspense used for?
- Pausing all rendering
- Catching errors
- Memoizing values
- Showing a fallback UI while lazy code or async data is loading
Answer: Showing a fallback UI while lazy code or async data is loading. Suspense displays a fallback (spinner, skeleton) while its children's code or data is still loading.
How do you provide the placeholder shown while a lazy component loads?
- A loading prop on the component
- The fallback prop of <Suspense fallback={...}>
- A useLoading hook
- A CSS class
Answer: The fallback prop of <Suspense fallback={...}>. Suspense takes a fallback prop: <Suspense fallback={<Spinner />}>...</Suspense>.
What happens if you render a lazy component with NO Suspense boundary above it?
- React throws an error because there is no fallback to show while it loads
- It renders instantly
- It renders an empty div
- Nothing, it is ignored
Answer: React throws an error because there is no fallback to show while it loads. Without a Suspense boundary React throws: a component suspended but no fallback UI was specified.
Why does lazy loading make an app's initial load faster?
- It compresses images
- It caches the DOM
- It shrinks the initial JavaScript bundle by fetching each chunk only when needed
- It disables JavaScript
Answer: It shrinks the initial JavaScript bundle by fetching each chunk only when needed. Code splitting downloads each screen's chunk on demand, so the first page ships less JS and paints sooner.
Two lazy children share one Suspense boundary; one loads in 100ms, the other in 500ms. When does the fallback disappear?
- At 100ms
- At 500ms — the boundary waits for all its children
- Immediately
- Never
Answer: At 500ms — the boundary waits for all its children. A boundary waits for ALL of its lazy children before revealing them, so the fallback hides at 500ms.
What kind of export must the module passed to lazy() have?
- A named export
- No export
- A const export
- A default export
Answer: A default export. lazy(() => import('./X')) expects X to have a default export; named exports need a wrapper.
What is the most common production use of React.lazy + Suspense?
- Animating elements
- Lazy-loading routes so each page is a separate chunk
- Validating forms
- Managing global state
Answer: Lazy-loading routes so each page is a separate chunk. Lazy routes split each page into its own chunk, downloaded only when the user navigates there.
Where should you create a lazy component?
- Inside the component body so it gets fresh props
- Inside a useEffect
- At module scope
- Inside an event handler
Answer: At module scope. Define lazy() at module scope; doing it inside the component body reloads the chunk on every render.