Code splitting in React

How to optimize your app performance and delivery better experiences for users.

•8 min read
Code splitting in React

Code splitting is a way to split up your code from a large file into smaller code bundles. These can then be requested on demand or in parallel.

Now, it isn't a new concept, but it can be tricky to understand.

When you're doing code splitting, it is important to keep the bundle sizes of the HTML, CSS, and JavaScript as small as possible. But often as applications scale larger bundles are unavoidable.

As an application grows, the complexity of it grows too, and CSS bundles, especially as the number and size of libraries increases. It can be split into multiple smaller files to mitigate downloading all files.

The current and desired scenario

Imagine that we have a single huge merged JavaScript file generated by WebPack and its responsible to load and make it available when its necessary (always 😅). The name of this file could be bundle.js.

Now, imagine how heavy this could be as our application grows faster.

Yes, it will not be performative at all.

Code Splitting for the rescue

Code splitting is a common practice in large React applications, and the increase in speed it provides can determine whether a user continues using a web application or leaves. Saving off even fractions of a second could be significant.

How does code splitting work in react?

The responsibility of Code-splitting is to adapt the application so that only the things that are currently needed by the user are loaded at a given time. This lazy loading job will bring a drastic reduction in the total amount of files, as well as a performance optimization. Overall, this will be an experience improvement for the user.

There are some alternatives, but the focus of this article will be on React.lazy() and React.Suspense() functions.

The Dynamic Imports approach

Dynamic import is a modern JavaScript feature that imports files similar to Promises. Some bundlers can parse dynamic import statements natively, while others require some configuration. The dynamic import syntax works for both static site generation and server-side rendering.

Dynamic imports use the then function to import only the code that is needed. Any call to the imported code must be inside that function.

How it works?

Loadable components come out of the box with a Loadable() function for creating asynchronous components that can be imported dynamically in a React application. Although it works better for server-side rendered applications. It can accept a fallback without the Suspense component. Loadable components use the Loadable() function to inject props from a component and support importing React components dynamically.

Check the code bellow.

import { Routes, Route } from "react-router-dom";
import loadable from "@loadable/component";
import Home from "./scenes/Home";

const LoadableScene = loadable((props) => import(`./scenes/${props.scene}`), {
  fallback: <div>Loading...</div>,
  cacheKey: (props) => props.scene,
});

export default function App() {
  return (
    <div className="App">
      <h1>React Loadable Component</h1>
      <Routes>
        <Route path="/" element={<AppLayout />}>
          <Route index element={<Home />} />
          <Route
            path="Gamification"
            element={<LoadableScene scene="Gamification" />}
          />
        </Route>
      </Routes>
    </div>
  );
}

Code splitting using React.lazy and React.Suspense 🔥

Finally, let's check the code-splitting with React.lazy() and React.Suspense() approach.

The React.lazy() function

The React.lazy() function allows you to render a dynamic import as a regular component. Basically, React.lazy() makes a call to a dynamic import and returns a promise. This can be seen below:

import React, { lazy } from "react";

const Blog = React.lazy(() => import("./Scenes/Gamification"));

See that in the code above, React.lazy() will bundle Gamification page when the component is rendered on a web page. The page is now loaded, this will ensure that a chunk of Gamification.js page is loaded when it's rendered.

In React, lazy components are usually rendered inside a Suspense component, which allows us to show fallback content, most times a loading icon, while users wait for a lazy component to load.

The React.Suspense() function

React.Suspense allows React developers to conditionally suspend the rendering of a React component until it is loaded. React.Suspense provides a fallback prop that accepts a React element which either is a JSX snippet or a React component.

When users visit a page using React dynamic imports, oftentimes they experience a blank page screen, this is done while the application loads the module, this can also cause errors as a result of slow internet connectivity for some users. React.lazy and React.Suspense when combined solves this issue for users.

To do this, use React.Suspense to suspend the rendering of a component until all dependencies are lazy-loaded, React.Suspense will also display your fallback UI for the user.

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

const Home = lazy(() => import('./Scenes/Home'));
const Gamification = lazy(() => import('./Scenes/Gamification');
const Shopping = lazy(() => import('./Scenes/Shopping');

const Routes = () => {
return (

<div>
  <Suspense fallback={<Loading />}>
    <Home />
    <Gamification />
    <Shopping />
      </Suspense>
    </div>
  );
};

Conclusion

As you can see, Code-splitting is a very useful resource when thinking about performance and improving the user experience for React Applications.

Vitor Britto
Buy Me A Coffee
Senior Software Engineer

Hello, I'm Vitor Britto 👋

With almost two decades of experience in software development, I have dedicated my career to creating elegant solutions for complex problems. Currently, I work as a Senior Software Engineer, focusing on web and mobile application development and best practices in software development.

I am passionate about sharing knowledge and contributing to the software development community. Through this blog, I share my experiences, learnings and insights about software development, architecture and modern technologies.

In addition to development, I am an enthusiast for clean code, design patterns and agile methodologies. I believe that the best software is not only functional but also sustainable and scalable.