React: From Components to Production · Performance and Modern React
Code Splitting with lazy and Suspense
Load code on demand to speed up the first page load.
Large bundles slow the first load. Split code with dynamic import(): React.lazy turns a module into a component loaded on first render, shown with a Suspense fallback. Split by route first (biggest win), then heavy rarely-used components (charts, editors, maps). Routers and frameworks support route-level splitting natively (React Router's lazy route property).
import { lazy, Suspense } from "react";
const AdminDashboard = lazy(() => import("./features/admin/AdminDashboard"));
const ChartPanel = lazy(() => import("./features/reports/ChartPanel"));
export function Reports({ showChart }: { showChart: boolean }) {
return (
<section>
<h1>Reports</h1>
{showChart && (
<Suspense fallback={<div className="skeleton chart" />}>
<ChartPanel />
</Suspense>
)}
</section>
);
}
// React Router route-level splitting
// { path: "admin", lazy: () => import("./features/admin/route") }
Common pitfalls
- Lazy-loading tiny components (more requests, no benefit).
- Declaring
lazy()inside a component (re-created every render). - No error handling for failed chunk loads after deploys.
Key points
- Split by route first
- lazy + Suspense for heavy components
- Handle chunk load errors
Further reading: react.dev: lazy
Practice questions
Why must lazy() be called at module level?
Answer
Calling it inside a component creates a new lazy component on every render, resetting state and refetching the module.
What happens to users with an open tab after a deploy removes old chunks?
Answer
Lazy imports may fail with chunk load errors. Catch them with an error boundary and offer a reload, or keep old assets available for a while.
Open in the app to mark lessons complete, take notes and track your progress.