Code Splitting with React Suspense

joshtronic

React.Suspense, along with React.lazy(), introduced in v16.6, adds new functionality that allows components to wait for something before actually rendering. This new ability makes code splitting and lazy loading of React components a breeze!

Getting Started

To get started, you will need to make sure you are on React v16.6 or higher. If you’re starting a new project, or if you’re working with an older project that’s not on the correct version, simply:

# Via npm
$ npm install --save react react-dom

# Or via Yarn
$ yarn add react react-dom

And then within the component you’d like to handle lazy loading of other components, import both Suspense and lazy:

import React, { lazy, Suspense } from 'react';

About Code Splitting

It’s worth pointing out that while Suspense and lazy allows you to lazy load components quickly and easily, you may not see much benefit out of the gate if you are bundling your entire application up into a single script with something like Webpack.

While out of scope for this article, you can read up on code splitting with Webpack in the Webpack documentation.

React.lazy()

The lazy() method takes a function as the first parameter which is expected to import another component, like so:

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

Because the import is inside of a function passed to lazy(), the loading of the component won’t happen until we actually use the component, instead of being eagerly loaded with the other imports at the top of your file.

React.Suspense

The React.Suspense component allows us to wrap a component up and specify a fallback component which will be shown while the wrapped component loads:

<Suspense fallback={<div>Loading...</div>}>
  <SomeComponent />
</Suspense>

This is especially beneficial when you are loading components that are either on the large side, or happens to import large libraries (like react-pdf or react-draft-wysiwyg).

Even if your components aren’t in the heavyweight division, lazy loading can help out your end users that may not have the best Internet connection.

Putting It All Together

To really get a feel for using React.Suspense and React.lazy() we are going to need two separate files. One is just our basic React app boilerplate, and the other is our component that will be lazy loaded.

Let’s start with the component we’re going to import. For this example, I’ve created a very simple component that loads the always dapper Alligator.io logo:

AlligatorioLogo.jsx

import React from "react";

function AlligatorioLogo() {
  const imageSource = "https://uploads.codesandbox.io/uploads/user"
    + "/39d6d9f6-60d2-4a60-9eb7-9250c3417bef"
    + "/saY6-alligatorio-logo.png";

  return (
    <img
      alt="Alligator.io Logo"
      src={imageSource}
      width="250"
    />
  );
}

export default AlligatorioLogo;

Next up is our App’s boilerplate code and a small bit of state logic so that we can manually trigger the loading of our component:

App.jsx

import React, { Component, Fragment, lazy, Suspense } from "react";
import { render } from "react-dom";

import "./styles.css";

const AlligatorioLogo = lazy(() => import("./AlligatorioLogo"));

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      showLogo: false
    };
  }

  onClick = () => {
    this.setState({ showLogo: !this.state.showLogo });
  };

  render() {
    return (
      <div className="App">
        <h1>React.lazy() + &lt;React.Suspense /&gt; Demo</h1>
        <div>
          <button onClick={this.onClick}>
            {this.state.showLogo &&
              <Fragment>Click to Hide the Logo</Fragment>
            }
            {!this.state.showLogo && (
              <Fragment>Click to Show the Logo</Fragment>
            )}
          </button>
        </div>
        <hr />
        {this.state.showLogo && (
          <Suspense fallback={<div>Loading...</div>}>
            <AlligatorioLogo />
          </Suspense>
        )}
      </div>
    );
  }
}

const container = document.createElement("div");
document.body.appendChild(container);
render(<App />, container);

As you can see, there’s really not much to using Suspense and lazy(). Because of how simple it is, it shouldn’t be much of a burden to port existing systems to it, especially if you’re already splitting out your components into separate files.

Conclusion

The introduction of React.lazy() and React.Suspense is a wonderful step forward in helping to reduce the amount of boilerplate code you need to write to build a modern web application.

While already quite powerful, Suspense isn’t without it’s shortcomings. The big missing piece, which is on the React Roadmap for a future v16 point release, is the ability to use Suspense with data fetching.

Once this data fetching functionality is available, Suspense will be able to wait for things like AJAX calls and show the fallback component while it loads.

It’s also worth noting that Suspense isn’t quite ready for use with server-side rendering.

If you’re ready to give React.Suspense and React.lazy() a spin, you can head on over to CodeSandbox to see the code from this article in action!

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#