Lazy Load a React Component on Click

Last Updated:
Lazy Load React Components on Click

Every since the days of working on WordPress (oh, the days of agency and hundreds of marketing sites) I have been focused on making all the sites I work on as fast as possible. This usually came down to making sure no excess/unneeded JavaScript was loaded.

 

 

TLDR

Use React.lazy in combination with useTransistion, and useState to defer a component until a button is clicked.

See the CodeSandbox for the solution.

 

Is a lot of JS needed for a complex form? Defer everything until a user interacts with one of the fields. Do we want a user to be able to play a small JavaScript game on the homepage? Wait until they press play to load the engine.

Web frameworks, libraries and processes have moved on since then, but the requirements are generally still the same. Make the website load fast so the chances of conversions are increased. The Trainline reduced their website load time by 0.3 seconds, and in doing so their revenue increased by an extra £8 million a year.

Ironically now most of my work is in React – this happens to be very heavy in JavaScript. So how can the website be made as light as possible?

The answer is defer and split out into chunks what isn’t needed. Then only load it when it is needed.

How To Split out and Load a React Component on Click

React Lazy

React has a function called lazy. This is generally seen in combination with the suspense component. However that is usually for deferring until page render.

lazy is a easy to use wrapper that makes it easy to create components that are loaded using dynamic import(). This automatically causes the chunked bundle containing the component to load when the component is rendered.

If there is no need to show fallback elements for whilst the component is loaded (like a spinner) then it is fine to use lazy by itself.

useTransistion

Before React 18 rendering was synchronous. This meant that was soon as the app began rendering, it would keep rendering until complete. Nothing could stop it.

With the updates in React 18 and concurrent rendering, React can pause rendering, abort or continue later.

useTransistion tells React that a particular state change will cause expensive rendering. React will then deprioritize this particular state change and let other smaller state changes to complete first. This results in a faster feeling UI.

This hook is important as showing a component will require a heavy render.

useState

useState will be used to handle the button click. It allows us to check if the button has been pressed or not, and trigger the render of the heavy component.

The Solution

The below code shows using React lazy to lazy load a component until a button is clicked. It is deferred until needed.

import { useTransition, useState, lazy } from "react";

// Use React.lazy to create a new chunk.
const HeavyComponent = lazy(() => import("./HeavyComponent"));

export default function App() {
  // useTransition is used to let React know there will be a
  // rerender and component load when the button is pressed.
  const [, startTransition] = useTransition();

  const [load, setLoad] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          startTransition(() => {
            setLoad(true);
          });
        }}
      >
        Load Component
      </button>
      {load && <HeavyComponent />}
    </>
  );
}

 

To see a working example of this, take a look at the CodeSandbox that shows lazy loading a React Component on click.

 

Hopefully this helped you, and if you have any questions you can reach me at: @robertmars

Related Posts

Helpful Bits Straight Into Your Inbox

Subscribe to the newsletter for insights and helpful pieces on React, Gatsby, Next JS, Headless WordPress, and Jest testing.