Code Splitting in React using Webpack

JALAJ MINDA
Prod.IO
Published in
5 min readApr 11, 2018

Client-side web development has gone through many design changes right from dumping all the code (including PHP + HTML +CSS + JS etc ) into one single file to writing modular code where we break the code into smaller pieces to ease the development process. Similarly, ways to ship the code to client-side has gone through major advancements. Nowadays common practice for single page application (SPA) is to bundle all these files into a single JS file(mostly known as bundle file) and ship it to the client.

Why should we use single file??

Because sending all these individual modular files over HTTP will create huge network overhead for the user accessing our web app. As the app grows over time with the inclusion of new features so will our code. Now the question that arises is, is this the right way? Aren`t we adding extra load to the network by sending the entire app code at once, instead of sending only what required for the user? Since every kilobyte matters, these days aren’t we slowing down the initial load time and ruining the first impression?

Yes! Therefore we need to find a middle ground where we are able to ship enough to the user for him to get started and send later bits “on demand”. This is done via “code-splitting”.

Definition of the code-splitting by Addy Osmani:

Code-splitting is one answer to the problem of monolithic bundles. It’s the idea that by defining split-points in your code, it can be split into different files that are lazy loaded on demand. This improves startup time and helps us get to being interactive sooner.

I am using Webpack module bundler here. It is smart enough to figure out the chunks automatically when it scans the application code during bundling process. To begin with, I am separating the vendor code from application code(actual code written by us) by providing multiple entry points in the webpack.config.js file. By doing this, I can cache the vendor bundle at client side as it does not change much over the time. Therefore, the monolithic bundle is now broken into two parts bundle.js and vendor.bundle.js. Great! However, I am still shipping my entire code to the client in bundle.js which is not desired.

Now we will see how we can further reduce and split the bundle.js with the help of Webpack. Concept: During the process of bundling Webpack reads through the source code and understand which bits need to be bundled separately on encountering dynamic import() function, hence creating meaningful chunks (separate javascript files). These chunks are then loaded to client-side on-demand. Unlike traditional ways of importing modules statically resulting in a single bundle, now bundles will be imported dynamically resulting in multiple bundles.

I will be doing code splitting at 2 levels

a) Route level

b) Component level

Route level code splitting

This is the most intuitive approach towards code splitting. The idea here is to break the monolithic bundle into bundles specific to routes of the app. I am using react-router — 2.4.1 in my app. Before we deep dive lets have a look at the router file where components are imported statically.

before

My app has two routes namely “intro” and “completed” kept in Router.jsx file. This is will result in a monolithic bundle, let’s change this code so that bundling process results in multiple bundles and load them for client only when asked for. Below is the updated router file, where I am using getComponent() function provided by the react-router along with dynamic import() to break and load the components.

after

Here for each route, I have replaced the prop component of <Route /> from component name to getComponent() function, it accepts two arguments nextState and a callback function. Here in the import() function, we pass the path of the component. Let’s scan the bundling process here — Webpack starts by traversing through each line of code and whenever import() is encountered, Webpack is smart enough to start creating a new bundle for the components within it. Therefore my app has resulted in 3 bundles, one for “intro” route component and its parts, second for “completed” route component and its parts, third for the code which is not part of either of the route.

Note that, Webpack will internally track the bundles and their interconnection; we need not worry about it. So here whenever any route is hit, corresponding getComponent() function is executed. import() function is executed which acts as a promise here. The corresponding bundle will be served to the client which was made during the bundling process.

Wait, why is this still breaking?

Note: We need to add babel plugin to support for dynamic import() in our app by using syntax-dynamic-import.

Component-level code splitting

In this approach, I will further split the route level bundles into smaller logical bundles. Let us look at sample code where I have a HomeScreen component consisting of a view profile button. On click of the button, details of the user must be displayed on the screen. Let’s have a look at this component before moving forward.

before

We are importing the modules statically, for example, the UserProfile component is bundled along with HomeScreen component. Let me show you how you can break this into separate chunks and load it on-demand. Here again, I am using the power of import() do the “magic”. I have created a utility component “LazyLoad” which will help me in separating the components into multiple bundles and loading them when encountered. Let us look at the LazyLoad component.

LazyLoad.jsx

After looking at what a LazyLoad component is, let’s use the LazyLoad component to call UserProfile component from HomeScreen component.

after

Viola! Code splitting of our app is completed.

Wait…… What the heck, how?

Here is a look at what just happened now. I am now calling LazyLoad component instead of the UserProfile component from HomeScreen Component. getComponent() function is passed as props to LazyLoad component which is executed in componentDidMount() of LazyLaod. This function uses dynamic import() to import the UserProfile component on-demand. I have mentioned the path to the UserProfile component to import function. Thus with the help of import function, we can break our single bundle into multiple bundles.

Note: Although breaking into multiple bundles is a good practice, we must do a thorough research as in at what places does asynchronous loading is essential.

The Initial size of my monolithic bundle was 2.7 MB which is broken into multiple bundles. The first bundle which has the basic code to serve the user is 482 KB for now.

Points to remember

I. Code splitting can be done at various level, here in the blog post it is been done 3 various places — 1.Separating vendor and application code. 2. Separating code based on routes. 3. Splitting the components into chunks.

II. import() function of javascript helps us in splitting the code into chunks and loading them to client side when demanded.

--

--