Code Splitting in React Using React Loadable

Code splitting is a technique used more in more with modern web app development that allows to load chunks of code only when needed. For example, with route-based code splitting, a user can be navigating to different routes of an app and behind the scenes the code for each route only loads upon accessing for the first time. With code splitting, only the minimal amount of code can be loaded when the app initializes and additional code is loaded on demand. This can help tremendously with performance by allowing for a reduced initial bundle size.

React Loadable is a library by @jamiebuilds that makes it easy to implement code splitting in React and that embraces React’s component model. It accomplish its magic using dynamic imports and webpack automatically splits dynamic imports into separate chunks when bundling.

Let’s quickly go over how to use React Loadable.

Installation

Just add the react-loadable package to your project using npm or Yarn:

$ npm install react-loadable

# or
$ yarn add react-loadable

Usage

Let’s create a contrived example to illustrate how simple Loadable is. First, SomeComponent:

components/SomeComponent.js

import React from 'react';

function SomeComponent() {
  return <h1>Some Component! 💣🚀👨‍🎤🤘</h1>;
}

export default SomeComponent;

And then let’s use it in our App component:

App.js

import React, { Component, Fragment } from 'react';
import SomeComponent from './components/SomeComponent';

class App extends Component {
  state = {
    showComponent: false
  };

  handleClick = () => {
    this.setState({
      showComponent: true
    });
  };

  render() {
    if (this.state.showComponent) {
      return <SomeComponent />;
    } else {
      return (
        <Fragment>
          <h1>Hello!</h1>
          <button onClick={this.handleClick}>Click me!</button>
        </Fragment>
      );
    }
  }
}

export default App;

Notice how the component is only rendered into the view once the user clicks on the button. Obviously with such a simple component it doesn’t make a difference, but with larger components in an app that’s non-trivial, code-splitting that component away can be a good idea.

Let’s refactor a bit to code split using React Loadable:

App.js

import React, { Component, Fragment } from 'react';
import Loadable from 'react-loadable';

function Loading() {
  return <h3>Loading...</h3>;
}

const SomeComponent = Loadable({
  loader: () => import('./components/SomeComponent'),
  loading: Loading
});

class App extends Component {
  state = {
    showComponent: false
  };

  handleClick = () => {
    this.setState({
      showComponent: true
    });
  };

  render() {
    if (this.state.showComponent) {
      return <SomeComponent />;
    } else {
      return (
        <Fragment>
          <h1>Hello!</h1>
          <button onClick={this.handleClick}>Click me!</button>
        </Fragment>
      );
    }
  }
}

export default App;

The Loadable higher-order component takes an object with two keys: loader and loading:

  • loader: Expects a function that returns a promise that resolves to a React component. Dynamic imports using import() return a promise, so all we have to do is point to the location of the component to load.
  • loading: Expects a component to render while the code is being loaded.

And it’s as easy as that! If you have a look at the Network tab in your browser’s Devtools, you’ll see the chunk being loaded when the button is clicked.

Loading Delay

If components are loaded quickly, having an intermediary loading component can become a source of visual annoyance in the UI. Luckily, Loadable provides an easy workaround in the form of a pastDelay prop that’s passed to the loading component and that evaluates to true once a certain delay has passed:

// ...

function Loading({ pastDelay }) {
  return pastDelay ? <h3>Loading...</h3> : null;
}

const SomeComponent = Loadable({
  loader: () => import('./components/SomeComponent'),
  loading: Loading
});

// ...

The default delay is 200ms, but you can change that by passing a delay config to the Loadable HOC. Here for example we change the maximum delay to 60ms:

function Loading({ pastDelay }) {
  return pastDelay ? <h3>Loading...</h3> : null;
}

const SomeComponent = Loadable({
  loader: () => import('./components/SomeComponent'),
  loading: Loading,
  delay: 60
});

Errors

Another prop gets passed-in to the loading component, error, which makes it easy to render something else if an error occurs while trying to load the component:

function Loading({ error }) {
  if (error) {
    return 'oh-noes!';
  } else {
    return <h3>Loading...</h3>;
  }
}

const SomeComponent = Loadable({
  loader: () => import('./components/SomeComponent'),
  loading: Loading
});

Route-Based Code Splitting

Oftentimes the easiest way to code-split an app is at the route level. Since routes in React when using React Router v4 are just components, it’s just as easy to use React Loadable to load the code for the different routes on-demand.

Here we’ll go over a simple example of route-based code splitting using React Loadable. First, make sure that we also have react-router-dom available in our project:

$ npm install react-router-dom

# or
$ yarn add react-router-dom

In the following example, we import the Dashboard component statically as it’ll be needed immediately on the root route and we use Loadable to only load the Settings and AddUser components when their respective route is activated:

import React, { Component } from 'react';
import Loadable from 'react-loadable';
import { Link, Route, BrowserRouter as Router } from 'react-router-dom';

import Dashboard from './components/Dashboard';

function Loading({ error }) {
  if (error) {
    return 'Oh nooess!';
  } else {
    return <h3>Loading...</h3>;
  }
}

const Settings = Loadable({
  loader: () => import('./components/Settings'),
  loading: Loading
});

const AddUser = Loadable({
  loader: () => import('./components/AddUser'),
  loading: Loading
});

class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <Link to="/">Dashboard</Link>
          <Link to="/settings">Settings</Link>
          <Link to="/add-user">Add User</Link>

          <Route exact path="/" component={Dashboard} />
          <Route path="/settings" component={Settings} />
          <Route path="/add-user" component={AddUser} />
        </div>
      </Router>
    );
  }
}

export default App;

🏇 And with this, you should be off to the races with code splitting! Have a look at the project's Readme for a mode in-depth look at the API and for documentation on advanced topics such as server-side rendering.

  Tweet It

🕵 Search Results

🔎 Searching...