This post is a practical approach to the concept of code splitting in frontend apps with React. We will learn why the code splitting concept exists, its advantages and how you can employ the concept in your React apps. We will also compare scenarios where splitting was employed and where it wasn’t so as to get a clearer understanding of why code splitting is so important.
In case you are still wondering on why you should care about this, then load speed is one concern to consider. To give the user a feeling of a more responsive network request, you need to start by reducing the size of data they have to download as much as possible. 51% of mobile internet users say that they’ve encountered a website that crashed, froze, or received an error.
What gets in the way of learning code splitting most times is the confusion that two features in webpack are trying to contradict each other. They are asset bundling and code splitting. Before we dive into splitting, let’s get this confusion out of your way.
What you need to know
The only knowledge required to get on with this article is fundamentals of:
- JavaScript
- React
- webpack
Asset bundling
This is actually why tools like webpack and browserify existed in the first place. The first thing that comes to your mind when you hear their names is “asset bundling”. Bundling is the act of merging your browser assets into one file so that the amount of HTTP requests and calls to the server are reduced.
For example, I could have a math.js
file and an index.js
file just for structure. The math.js
file defines a method that computes values while the index.js
is where the method is called:
math.js
export const add = (a, b) => a + b
export const subtract = (a, b) => a - b
index.js
import { add, subtract } './math.js'
const a = 5;
const b = 3;
console.log(add(a,b))
console.log(subtract(a,b))
When we run index.js
through a bundler like webpack, it recursively resolves all of the dependencies and merges them into a single file that could be called bundle.js
:
const add = (a, b) => a + b
const subtract = (a, b) => a - b
const a = 5;
const b = 3;
console.log(add(a,b))
console.log(subtract(a,b))
Just like I mentioned, this approach helped reduce the trips we make to a server, hence an attempt to optimize the experience.
Code splitting
Code splitting is a feature that helps you add split points in your code based on the user journey. These split points are then downloaded when the user embarks on the related journey or is about to.
For example, your split points can be routes where you download each route code when the user visits or wants to visit a route. The split point can also be a component or function that does a heavy computation. You can render other parts of a page and then asynchronously load the heavy component while providing a good pending user experience.
Since HTTP v2 is becoming more popular, code splitting now has more opportunity to shine better as HTTP v2 favors multiple smaller asset downloads to a bundled heavy download.
Let’s see an example that makes use of routes as split points. We will build the app without splitting and measure, then we’ll add code splitting, measure it again and let the metrics judge which is better.
Create a React app
Start with creating a React app using the create-react-app
CLI tool:
## Install the CLI tool if you don't already have it
npm install -g create-react-app
## Create a new React project
create-react-app react-router-code-splitting
Install the following dependencies to the new React app:
## cd into the folder
cd react-router-code-splitting
## Install dependencies
npm install --save react-router-dom react-loadable react-spinkit
We will go into the details of what each dependency is but here is a short note on them:
react-router-dom
is the React Router for the browser or DOM environment. We will use this to set up routing in the app.react-loadable
is a library that makes code splitting in React easy to work.react-spinkit
is a loading component library for React
Update src/index.js
to set up the router:
//...
import { BrowserRouter as Router } from 'react-router-dom';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
Create two components to serve as the pages for our routes named home and about:
src/Home.js
import React from 'react';
const Home = () => (
<div>
<h2>Home page</h2>
<p>....</p>
</div>
)
export default Home;
src/About.js
import React from 'react';
const About = () => (
<div>
<h2>About page</h2>
<p>....</p>
</div>
)
export default About;
Next import these components in src/App.js
so as to use them for the routes:
import { Route, Switch, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
We are also importing Route
, Switch
, and Link
from the router library. We will use this components to create routes and link to them. We will do this in the App.js
render method:
render() {
return (
<div className="App">
<p>
<Link to="/">Home</Link> |
<Link to="/about">About</Link>
</p>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
</Switch>
</div>
);
}
Splitting route code
If we introduce split points at the routes level, the browser will only download code needed by that route and not all the bundled code. We will use the react-loadable
and react-spinkits
libraries to achieve this.
Replace the Home
and About
import lines with the following:
import Loadable from 'react-loadable';
import Spinner from 'react-spinkit';
const Loading = () => <Spinner name="double-bounce" />;
const Home = Loadable({
loader: () => import('./Home'),
loading: Loading
});
const About = Loadable({
loader: () => import('./About'),
loading: Loading
});
Loadable
takes an object as an argument. The object must be passed a value for the loader
and loading
properties. The loader
is the component we want to load asynchronously.It is achieved using the [dynamic](https://developers.google.com/web/updates/2017/11/dynamic-import)[](https://developers.google.com/web/updates/2017/11/dynamic-import)[import](https://developers.google.com/web/updates/2017/11/dynamic-import) method. loading
is the component that Loadable
will show while waiting for the loader
component to get ready. The common use case here is to show a loading UI which you can achieve with react-spinkit
.
Add a Contact component in the src
folder named Contact.js
:
import React from 'react';
const Contact = () => (
<div>
<h2>Contact page</h2>
<p>......</p>
...
</div>
)
export default Contact;
Load the component in the App.js
file:
const Contact = Loadable({
loader: () => import('./Contact'),
loading: () => Loading
});
Then add a route that renders the component as well as a link that points to it:
<div className="App">
<p>
<Link to="/">Home</Link> |
<Link to="/about">About</Link> |
<Link to="/contact">Contact</Link>
</p>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</div>
Note that most times, the bundle file will be cached since it hardly changes. The only speed you would have to worry about is that of the smaller chunks that are representing each of the routes we created in this project.
Conclusion
Performance is the web trend these days and if you want to stay competitive with your web product, you need to make sure that the app is fast or at least your users perceive it as being fast. Code splitting is one way to boost your app’s performance among other things like PWA which you should check out.