React.lazy() takes a function that calls the standard dynamic import() syntax, returning a promise for the component's module, and produces a component that can be rendered normally but triggers its underlying code chunk to actually download the first time it's rendered, rather than being included in the application's initial bundle. Because that download is asynchronous, a lazily-loaded component must be wrapped in a Suspense boundary somewhere above it, which displays a fallback UI while the chunk is still loading — this pattern, called code-splitting, is especially valuable for large components or entire routes that aren't needed on the initial page load, meaningfully shrinking the amount of JavaScript a user has to download upfront.
1Understanding Performance Optimization with Lazy Loading
React.lazy() takes a function that calls the standard dynamic import() syntax, returning a promise for the component's module, and produces a component that can be rendered normally but triggers its underlying code chunk to actually download the first time it's rendered, rather than being included in the application's initial bundle. Because that download is asynchronous, a lazily-loaded component must be wrapped in a Suspense boundary somewhere above it, which displays a fallback UI while the chunk is still loading — this pattern, called code-splitting, is especially valuable for large components or entire routes that aren't needed on the initial page load, meaningfully shrinking the amount of JavaScript a user has to download upfront.
Code-split entire routes with React.lazy() rather than individual small components — splitting a whole page/route that isn't needed until the user actually navigates there typically gives a much more meaningful reduction in initial bundle size than lazy-loading small, already-cheap components.
import React, { Suspense } from 'react';
const Settings = React.lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<p>Loading settings...</p>}>
<Settings />
</Suspense>
);
}2Practical Example
Here is a real-world application of Performance Optimization with Lazy Loading showing how it is used in production React code.
// Without lazy loading, Settings' code is part of the main bundle from the start
// import Settings from './Settings';
// With lazy loading, it's a separate chunk downloaded only when rendered
const Settings = React.lazy(() => import('./Settings'));3Best Practices
Follow these guidelines when working with Performance Optimization with Lazy Loading:
1. Apply React.lazy() to entire routes or large, infrequently-needed components, like a rarely-opened modal or admin panel, rather than small components where the splitting overhead outweighs the benefit
2. Always wrap a lazily-loaded component in a Suspense boundary with an appropriate fallback, since React throws an error if a lazy component renders without one somewhere above it
3. Combine React.lazy() with your router's own route-based code-splitting conventions when using a framework like React Router or Next.js, rather than reinventing route-level splitting manually
Tip: Code-split entire routes with React.lazy() rather than individual small components — splitting a whole page/route that isn't needed until the user actually navigates there typically gives a much more meaningful reduction in initial bundle size than lazy-loading small, already-cheap components.
import React, { Suspense } from 'react';
const Settings = React.lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<p>Loading settings...</p>}>
<Settings />
</Suspense>
);
}