In modern JavaScript applications, bundle size can become a significant issue. Users don’t want to download a large package of JavaScript just to load the first page of an app. By default, when you ship a Web App built with Webpack bundling, the bundle contains code that might never be executed if the user only interacts with a specific page. One way to address this problem is through code splitting.

Code splitting is the practice of loading only the JavaScript that is needed at the moment it is needed. This approach improves app performance, reduces memory and battery usage on mobile devices, and minimizes the amount of data that needs to be downloaded.

React 16.6.0, released in October 2018, introduced a new way of performing code splitting using the React.lazy and Suspense features.

React.lazy is used to import any component in a lazy manner. For example:

import React from 'react';

const TodoList = React.lazy(() => import('./TodoList'));

export default () => {
  return (
    <div>
      <TodoList />
    </div>
  );
}

In the above code, the TodoList component will be dynamically added to the output as soon as it becomes available. Webpack will create a separate bundle for it and load it when necessary.

Suspense is a component that can be used to wrap any lazily loaded component:

import React from 'react';

const TodoList = React.lazy(() => import('./TodoList'));

export default () => {
  return (
    <div>
      <React.Suspense fallback={<p>Please wait</p>}>
        <TodoList />
      </React.Suspense>
    </div>
  );
}

The Suspense component handles the output while the lazily loaded component is being fetched and rendered. You can use the fallback prop to show a loading indicator or any JSX component during the loading process.

This code splitting approach works seamlessly with React Router as well. Here’s an example:

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const TodoList = React.lazy(() => import('./routes/TodoList'));
const NewTodo = React.lazy(() => import('./routes/NewTodo'));

const App = () => (
  <Router>
    <React.Suspense fallback={<p>Please wait</p>}>
      <Switch>
        <Route exact path="/" component={TodoList} />
        <Route path="/new" component={NewTodo} />
      </Switch>
    </React.Suspense>
  </Router>
);

By leveraging React.lazy and Suspense, you can significantly improve the performance of your React app, reduce the bundle size, and create a more dynamic and efficient user experience.