This post is also available in different formats so you can read on the go or share it around!
How to leverage code-splitting easily in React 16
Code-splitting is now easier than ever thanks to the work the React team has done on Suspense. If you aren’t code-splitting today, let’s take a look at why you might want to split code, what it means, and how to achieve it quickly and easily.
Why use code-splitting?
If you’re serving your frontend code in a single bundle, every feature and dependency increases the size of that bundle. A larger bundle means more code which is being sent to your users. This can impact users who are on mobile or low powered devices and especially impacts users with data caps. There are many solutions to this problem but one that has a lot of great possibilities is code-splitting. Code-splitting is the practice of separating out your code bundle. The goal is to serve the appropriate JavaScript bundle to the user and nothing more. For a single page web app, this can be essential to providing a responsive user experience and first impression. An app that needs to download a large monolithic bundle before it can provide any value to the user is of little use. A user is unlikely to be tolerant when staring at a blank screen for 5 seconds on their mobile device waiting for your app to load. They will likely go to your faster competitor.
Load only what you need, when you need it.
Code-splitting is especially useful when some routes and/or components need to use a heavy library in places throughout your app. You may have a landing page that is very lightweight and a dashboard that displays a lot of data with charts and data visualizations that rely on heavier libraries. You shouldn’t need to load both when the user is requesting the landing page. So how can we start code-splitting in React 16?
How do I use code-splitting?
Code-splitting is easier than ever with React 16.6.0 onward. It is now built into React and requires very little setup to deliver a great experience. We need to wrap our component that we want to load lazily with <Suspense />
. Then we need to make a few subtle changes.
When React comes to render MyComponent, it will attempt to import it. The Suspense wrapper around the component will provide a fallback whilst the component is loading and swap it out when it’s complete.
React Suspense Lazy Loading Example
Here is an example of a router with 3 components Dashboard, Locales and Flow that are tied to routes of the same name and each use a large library.
import React from "react"
import Home from "./Home.jsx"
import Nav from "./Nav.jsx"
import { Router } from "@reach/router"
// Using React.lazy and React.Suspense to load components when requested
const Dashboard = React.lazy(() => import("./Dashboard.jsx"))
const Locales = React.lazy(() => import("./Locales.jsx"))
const Flow = React.lazy(() => import("./Flow.jsx"))
export default () => (
<div>
<Nav links={["Dashboard", "Locales", "Flow"]} />
<div className="container" style={{ paddingTop: "6rem" }}>
{/* The fallback prop will show a component when loading */}
<React.Suspense fallback={<p>Loading...</p>}>
<Router>
<Home path="/" />
<Dashboard path="/dashboard" />
<Locales path="/locales" />
<Flow path="/flow" />
</Router>
</React.Suspense>
</div>
</div>
)
The router wrapped in a Suspense component to lazily load some more sizable routes
Take a further look at an example I created that shows code-splitting using React.lazy()
. My example uses Parcel but it’s is just as easy to get it working with your favourite blunder like Webpack. The example illustrates using larger dependencies that you might want to include for very specific purposes. These are ideal for code-splitting because they can be logically separated where each dependency is isolated to its own route.
Demo
Take a look at the demo yourself, the full repository is available on my GitHub.
Generating a detailed report with Parcel
Using parcel build --detailed-report
will show the bundles and their sizes. This is useful for seeing how your bundles are composed and can be used to identify large bundles.React Suspense Lazy Loading - Parcel Detailed Report
*Repo: https://github.com/Darth-Knoppix/react-suspense-lazy-loading-example Demo…*asciinema.org
Single Bundle Detailed Report
The final bundle size without source maps is approximately 425KB.
Code-splitting Detailed Report
With code-splitting, Parcel has generated 4 bundles. The dashboard is the largest at approximately 210KB, as it uses a large chart library. We can avoid loading the dashboard when the user is visiting one of the other routes. Visiting the /flow
route will render our Flow component which uses the flowpoints library and is only around **18KB. **Savings like these mean users can get to using your app quicker. We only send what we need to the client.
Takeaway
Code-splitting is a great way to reduce the burden of a single page web app on the end user. As these web apps grow in complexity and size, users still expect a snappy experience and code-splitting is one tool we, as developers, have at our disposal. Sending only what the user needs when they need it saves time and money. React 16 makes these features intuitive to implement with React.lazy()
and Suspense
. With a few simple tweaks, you can achieve great performance gains without sacrificing user experience.
Resources:
If you are interested in more React 16 features then take a look at “React Hooks for Greater Good”, for more performance-related tips for React— take a look at how to “Save user’s data with a lighter alternative to React”. Preact is a great framework which can reduce your overall bundle size even more while being largely compatible with React.
Let me know if there is anything I can do to improve or topics I can investigate in more depth. Thanks for reading!
A Fullstack Software Engineer working with React and Django. My main focus is JavaScript specialising in frontend UI with React. I like to explore different frameworks and technologies in my spare time. Learning languages (programming and real life) is a blast.