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.