HomeAbout Me

React Performance 4: Code Splitting

By Daniel Nguyen
Published in React JS
July 05, 2025
3 min read
React Performance 4: Code Splitting

Code Splitting

Code splitting acts on the principle that loading less code will speed up your app. Say for example that we’re building a complex dashboard application that includes the venerable d3 library for graphing data. Your users start complaining because it takes too long to load the login screen.

So, considering that performance problems can be resolved by less code, how can we solve this one? Well, do we really need to have that code for the chart when the user loads the login screen? Nope! We could load that on-demand.

Luckily for us, there’s a built-in way to do this with JavaScript standards. It’s called a dynamic import and the syntax looks like this:

import('/some-module.js').then(
(module) => {
// do stuff with the module's exports
},
(error) => {
// there was some error loading the module...
},
)

📜 Learn more about dynamic imports in the browser in Super Simple Start to ESModules in the browser

To take this further, React has built-in support for loading modules as React components. The module must have a React component as the default export, and you have to use the <Suspense /> component to render a fallback value while the user waits for the module to be loaded.

// smiley-face.tsx
export default function SmileyFace() {
return <div>😃</div>
}
// app.tsx
import { lazy, Suspense } from 'react'
const SmileyFace = lazy(() => import('./smiley-face.tsx'))
function App() {
return (
<div>
<Suspense fallback={<div>loading...</div>}>
<SmileyFace />
</Suspense>
</div>
)
}

🦉 One great way to analyze your app to determine the need/benefit of code splitting for a certain feature/page/interaction, is to use the “Coverage” feature of the developer tools.

lazy

👨‍💼 Our app has a neat Globe component that shows the user where they are on the globe. Cool right? It’s super duper fun.

But one day users started complaining the app is taking too long to load. We’re using several sizeable libraries to have the really cool globe, but users only need to load it if they click the “show globe” button and loading it ahead of time makes the app load slower.

So your job as a performance professional is to load the code on-demand so the user doesn’t have to wait to see the checkbox.

For this one, you’ll need to open the solution in isolation and open the Chrome DevTools Network tab to watch the JavaScript chunks load when you click “show globe.” Your objective is to have the network load those same chunks so they’re not in the bundle to begin with.

💰 Here’s a quick tip: In the Network tab, there’s a dropdown for artificially throttling your network speed. It defaults to “Online” but you can change it to “Fast 3G”, “Slow 3G”, etc.

Also, spend a bit of time playing with the coverage feature of the dev tools.

Eager Loading

👨‍💼 So it’s great that the users can get the app loaded faster, but it’s annoying when 99% of the time the reason the users are using the app is so they can interact with our globe. We don’t want to have to make them wait first to load the app and then again to load the globe. Wouldn’t it be cool if we could have globe start loading as soon as the user hovers over the checkbox? So if they pointerOver or focus the <label> for the checkbox, we should kick off a dynamic import for the globe module.

See if you can make that work.

Tip: It doesn't matter how many times you call `import('./path-to-module')`, vite will only actually load the module once.

Transitions

👨‍💼 By default, when a component suspends (like our lazy component is while we’re lazy-loading some code), React will render the fallback of our suspense boundary immediately. Go ahead and try it now. Hover over the checkbox for long enough for the code to load and then check it. You’ll notice even though the code is ready to go, it still shows our fallback UI.

The reason this happens is React wants to help us avoid a flash of loading state because it doesn’t know whether our content will be ready immediately or not. So React shows the fallback immediately, then when it sees the content is already ready, it will actually wait a little bit before switching from the fallback to the real content. This is a good default, but it’s definitely not the best user experience for our situation.

So instead, we can wrap our state update setShowGlobe in a transition so React doesn’t go to the suspense fallback and instead we can show a pending UI if we so decide.

This is similar to what we’ve done in the past, so if you’d like you can add useSpinDelay from spin-delay to prevent pushing the flash of loading state to later if the globe’s code isn’t quite ready yet.

📜 useTransition


Tags

#React

Share

Previous Article
React Performance 3: Concurrent Rendering

Table Of Contents

1
Code Splitting
2
lazy
3
Eager Loading
4
Transitions

Related Posts

React Testing 8: Testing custom hook
September 09, 2025
1 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media