React.lazy and Suspense make code-splitting easy

React.lazy and Suspense make React developer life easy. React.lazy helps in code-splitting and Suspense backup React.lazy in loading state. Before, we go into details let’s start things simply. let’s be familiar with some background knowledge.

What is Bundling?

Bundling is the process of putting code in many files into one file. While bundling is essential in modern web applications for optimization, the size of the bundled file can grow as the application grows with many lines of code incorporating third-party libraries and modules. Having one such large bundle can increase the load time, especially on a slower network leading to poor user experience. It is also difficult to track and fix errors with such bundled files.

So, what is the solution?

One of the solutions is code-splitting

Code-splitting and Bundlers

Code-splitting is a technique to first load the minimum code needed and then the rest of the code as it is required. This prevents the loading of unnecessary code initially thereby decreasing load time.

If you can remember, a similar approach was used with libraries such as Jquery when the Jquery  CDN scripts were placed at the end of the file rather than at the head.

Code-splitting creates multiple bundled files. These files, however, will not load all at once. As mentioned before, only the required file will load at run time and the rest of the bundles will run on demand. This dramatically improves the performance of web applications.

So, who is doing this process? Well, there are bundlers to help this process. Some of the most popular ones are Webpack, Parcel, and Rollup.

While you can configure these bundlers by yourself, many developers would use tools such as creat-react-app, and vite.js to set up the development environment. These tools can save a lot of time and let you focus on development rather than putting your time into configuring bundlers. Anyway, at least a basic understanding of configuring bundlers can be helpful in fixing errors associated with setting up the development environment.

Dynamic import in JavaScript

Dynamic import in JaVascript can help with code-splitting. 

Let’s take a look at an example

 import MyComponent from './components/MyComponent'

let’s say I do not need this component when my web app starts. I need it to render on a button click.  If I use static import ( as above ) MyCompents will be loaded initially to the browser whether I use it or not, and this will be initially included in the bundled file. You can check this on the Network tab and click on js on Chrome dev tools.

Network ->js on chrome dev tools

But, if I import this component with dynamic import, things change. lets ‘s see an example.

import React, { useState } from 'react';

function Demo() {

  const [Component, setComponent] = useState(null);
  const handler = ()=>{
    import('./components/MyComponent').then(module => {          
        setComponent(module.default);
    });

    if (!Component) {
      return <div>Loading...</div>;
    }
  }

  return(
  <>
      <button onClick={ handler }>Render</button>
      <br /><br />
      { Component }
  </>
  )
}

export default Demo;

In the demo program above, I am rendering MyComponent on a button click. Therefore, I do not need that component at the start. When I start this demo App, the Network tab on dev tools is as below.

There is no MyComponent component loaded in the browser.  This is because I am importing it with dynamic import. It only loads only when a click event occurs. 

But, if you write the same code above with static import even before the onClick event occurs, the component will be loaded in the browser and will be rendered when you click on the button

The dynamic import in JavaScript is executed asynchronously. Therefore, the if statement in the above code displays a “loading message” in the delay between when the event is triggered by the user and when the component is rendered. You may not see the message in this case as it is such a small delay. 

React.lazy() function

React.lazy() is a function introduced in React v16.6. This function utilizes dynamic import to import React components asynchronously. React. lazy works asynchronously, thus, the component it imported will not be loaded in the browser until they are requested by the user. This helps improve the performance of React applications.

Now, let us see how to use React.lazy().

import { lazy, useState , useEffect } from 'react';

const Home = lazy(() => import('./components/Home'));
const Admin = lazy(() => import('./components/Admin'));
const Subscriber = lazy(() => import('./components/Subscriber'));

const Dashboard = () => {

    const [user, setUser] = useState('user');
    const [state, setState ] = useState(<Home />);    

    useEffect(()=>{
        switch( user ){
            case 'admin' :
                 return setState ( <Admin />);                        

            case 'subscriber':
                return setState( <Subscriber />);          

            default : return setState(<Home />);
            }

    },[user]);

    return(
          <>
            <button onClick={()=>setUser('home')}>Home</button>
            <button onClick={()=>setUser('admin')}>Admin</button>
            <button onClick={()=>setUser('subscriber')}>Subscriber</button>
            <br/><hr /> 
            <div>{ state }</div>
          </>
    )
};

export default Dashboard

UI of the demo App

In the above program, I render three components conditionally. Only the Home components and other necessary parent components and CSS files are rendered initially. The Admin and Subscriber components are initially not required. Thus, these components are not loaded. React.lazy() in conjunction with dynamic import prevent these components from loading. But, once you click on Admin or Subscriber button, these components will be loaded and rendered as you can see on the dev tools on your browser.

React.Suspense component

React.Suspense is a special component introduced to use with React.lazy(). It handles the loading state of the lazy-loading components. For example, It displays a “loading” message or a spinner while the lazy-loading components( components rendered on-demand and asynchronously)  are rendered on the browser. A fallback prop is passed to the Suspense component. ( fallback={<div>Loading…</div>} )

This component is experimental. In the future, it can have many use cases. You will be able to use this component in data-fetching operations. 

You can modify the above program as follows:

  1. Add Suspense to import statement in the code

import { lazy, Suspense, useState , useEffect } from 'react'

    2. Wrap the code within the return statement by the Suspense component 

return(
            <>
             <Suspense fallback={<div>Loading...</div>}>
                <button onClick={()=>setUser('home')}>Home</button>
                <button onClick={()=>setUser('admin')}>Admin</button>
                <button onClick={()=>setUser('subscriber')}>Subscriber</button>
                <br/><hr /> 
                <div>{ state }</div>
            </Suspense>   

             </>

    )

Final thoughts

React.lazy() and Suspense can be useful in performance optimization and positive user experience. performance optimization critical aspect of web apps to improve performance on slow networks and devices. It is also important, especially on large systems that use many third-party libraries, API,s and modules. So, Next time be as lazy as React.lazy.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top