React React.lazy

In the last lesson, React Code Splitting, you learned why splitting your bundle speeds up the first load. Now you’ll use React.lazy to load a component only when it’s first shown, so it ships in its own bundle.

🤔 What does React.lazy do?

Here’s the idea in one line. React.lazy lets you load a component lazily, meaning only when it’s actually rendered, not upfront.

  • Normally you import a component at the top, so it’s part of the main bundle.
  • React.lazy changes that to a dynamic import, loaded only when the component first appears.
  • Your build tool puts that component in its own separate bundle.
  • So the code for it isn’t downloaded until the user reaches the part that uses it.

So React.lazy is the tool that actually performs the code split for a component.

🧩 Normal import vs lazy import

Let’s compare. Here’s a normal import, then the lazy version.

// normal import - part of the main bundle, loaded upfront
import Dashboard from "./Dashboard";
// lazy import - its own bundle, loaded only when first rendered
import { lazy } from "react";
const Dashboard = lazy(() => import("./Dashboard"));

Read the lazy version carefully.

  • lazy(...) takes a function that returns a dynamic import().
  • import("./Dashboard") is the dynamic import; it loads the file on demand and returns a promise.
  • lazy wraps that, giving you a component you can use like any other.
  • The build tool sees the dynamic import and splits Dashboard into its own bundle automatically.

So the only change is swapping the static import for lazy(() => import(...)). After that, Dashboard is used exactly like a normal component.

It must be a default export

React.lazy expects the file to have a default export, which is the component. import("./Dashboard") resolves to that default. If your component is a named export, you’ll need a small extra step, but a default export is the simple, standard case.

⚠️ Lazy components need a Suspense wrapper

There’s one rule you can’t skip. A lazy component takes a moment to load, so you must wrap it in Suspense with a fallback to show during the wait.

import { lazy, Suspense } from "react";
const Dashboard = lazy(() => import("./Dashboard"));
function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<Dashboard />
</Suspense>
);
}

Walk through why this is required.

  • When Dashboard is first shown, its bundle has to download, which takes a moment.
  • During that moment, React shows the Suspense fallback, here “Loading…”.
  • Once the bundle arrives, React swaps the fallback for the real Dashboard.
  • Without a Suspense wrapper, React throws an error, because it has nothing to show while loading.

No Suspense, no lazy

A React.lazy component must be inside a Suspense boundary. If you forget it, React errors out. You’ll learn Suspense in detail next, but for now: every lazy component needs a Suspense with a fallback above it.

💡 The most common use: lazy routes

The biggest payoff is lazy-loading route pages, so each page’s code loads only when the user visits it.

import { lazy, Suspense } from "react";
import { Routes, Route } from "react-router-dom";
// each page is its own lazy bundle
const Home = lazy(() => import("./Home"));
const Dashboard = lazy(() => import("./Dashboard"));
function App() {
return (
<Suspense fallback={<p>Loading page...</p>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}

See what this achieves.

  • Home and Dashboard are each lazy, so each has its own bundle.
  • Visiting / loads only the Home bundle, not the dashboard’s code.
  • Going to /dashboard then loads that bundle, showing the fallback briefly.
  • The first load is smaller, because the user only downloads the page they opened.

So lazy routes are the classic, high-value use of React.lazy. The user downloads each page’s code just in time.

⚠️ Common Mistakes

The top mistake is using a lazy component with no Suspense around it.

// ❌ lazy component with no Suspense - React throws an error
const Dashboard = lazy(() => import("./Dashboard"));
return <Dashboard />;
// ✅ wrap it in Suspense with a fallback
return (
<Suspense fallback={<p>Loading...</p>}>
<Dashboard />
</Suspense>
);

Keep these in mind.

  • Don’t forget the Suspense wrapper. Every lazy component needs one above it.
  • Don’t lazy-load tiny, always-shown components. The benefit is for big or route-level pieces.
  • Don’t forget the file needs a default export for the simple lazy(() => import(...)) form.

✅ Best Practices

A few habits for React.lazy.

  • Use it for route pages and heavy components, not small always-visible ones.
  • Always wrap lazy components in a Suspense with a sensible fallback.
  • Make the fallback match the area, like a page-sized loader for a lazy route.
  • Keep lazy components as default exports for the simplest syntax.

One Suspense can cover many

You don’t need a separate Suspense for every lazy component. One Suspense boundary can wrap several lazy components, showing one fallback while any of them load. Place boundaries where a shared loading state makes sense.

🧩 What You’ve Learned

  • React.lazy loads a component only when it’s first rendered, not upfront
  • ✅ Use const X = lazy(() => import("./X")) instead of a normal import
  • ✅ The build tool splits that component into its own bundle automatically
  • ✅ A lazy component must be wrapped in a Suspense with a fallback, or React errors
  • ✅ The most common, valuable use is lazy-loading route pages
  • React.lazy expects a default export for the simple syntax

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does React.lazy do?

    Why: React.lazy turns a component into one that loads on demand. The build tool puts it in a separate bundle that downloads only when the component is first shown.

  2. 2

    How do you write a lazy import?

    Why: You use lazy with a function that returns a dynamic import: const X = lazy(() => import('./X')). Then you use X like a normal component.

  3. 3

    What must wrap a React.lazy component?

    Why: A lazy component takes a moment to load, so it must be inside a Suspense with a fallback to show during the wait. Without it, React throws an error.

  4. 4

    What is the most common, high-value use of React.lazy?

    Why: Lazy routes are the classic use. Each page becomes its own bundle, so the user only downloads the code for the page they actually open.

🚀 What’s Next?

Now you can load components on demand with React.lazy. Next you’ll learn Suspense properly: how the fallback works and how to place boundaries well.

React Suspense

Share & Connect