Skip to content

Lazy Loading

React.lazy loads components dynamically via import(). Wrap lazy components in <Suspense> with a fallback while the chunk downloads. Standard pattern for route-level code splitting.

Dynamic import() for Components

const Page = lazy(() => import('./Page')) tells the bundler to create a separate chunk loaded on demand when Page first renders.

Suspense fallback shows loading UI during chunk fetch — spinners, skeletons, or null for fast networks.

import { lazy, Suspense } from 'react';

const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <Settings />
    </Suspense>
  );
}

Each lazy import typically becomes its own JS chunk in Vite/Webpack output.

Lazy + Suspense Pattern

const Component = lazy(() => import('./Component'));

<Suspense fallback={<Spinner />}>
  <Component />
</Suspense>
  • lazy() must wrap a function returning import() promise.
  • Named exports: import('./mod').then(m => ({ default: m.Named })).
  • Suspense boundary required above lazy component.
  • Nested Suspense boundaries enable granular loading states.

Lazy Loading Reference

Common lazy loading patterns.

Pattern Example
Default export lazy(() => import('./Page'))
Named export .then(m => ({ default: m.Page }))
Route lazy element: lazy(() => import(...)) with Suspense
Fallback <Suspense fallback={...}>
Preload import('./Page') on hover/link intent
Error Error boundary around Suspense

Lazy Routes in React Router

Wrap route tree or individual route elements in Suspense. Some setups lazy-load page components at route definition.

const Dashboard = lazy(() => import('./Dashboard'));
<Route path="/dash" element={
  <Suspense fallback={<Spinner />}><Dashboard /></Suspense>
} />

Preloading on User Intent

Call dynamic import on link hover/focus to start loading before navigation — reduces perceived wait.

Common Mistakes

  • Forgetting Suspense wrapper around lazy components.
  • Lazy loading tiny components — overhead not worth it.
  • Not handling chunk load failures.
  • Default vs named export import mismatch.

Key Takeaways

  • React.lazy defers loading component code until needed.
  • Suspense provides fallback UI during chunk load.
  • Ideal for route-level and heavy feature splitting.
  • Combine with error boundaries for failed loads.

Pro Tip

Lazy load routes and heavy modals first — biggest bundle wins with least complexity.