Code Splitting, SSR, Lazy loading React components - a deeper understanding! (Part -1)

Parth Mahajan
Tata 1mg Technology
6 min readFeb 19, 2020

It’s 2020! Everyone is talking about code splitting and you are pumped up to implement it.

But, are you ready? Let’s find out!

https://unsplash.com/photos/oQh4FabMPs0

Before we directly jump into code-splitting, let’s see if we can answer a few questions:

  • Npm is flooded with libraries. Which one shall we go for?
  • What could be the best way of implementation?
  • If all of them work, why choose one over the other?
  • Are we clear about our architectural setup? And, does our library support it?

CONFUSED? 😟 — Let’s take one step at a time.

At 1mg, we bumped into many such problems while setting up code splitting for PWA. In this blog, we’ll be looking at how we approached the problem and implemented a perfectly tailored solution catering to our requirements.

Understanding the Architecture

First things first, we have to understand why is there a difference between the two rendering techniques and why should we even care about them?

If we are setting up a CSR (Client-Side Rendered) architecture, we are actually generating our HTML on the client’s browser by executing javascript. The server does not send us anything more than just a skeleton. So, we don’t have to worry about how to make our rendering server understand our bifurcated components as it won’t render those chunks.

But on the other hand, going for a Server Side setup or an Isomorphic structure, we want to get an HTML that has our components pre-rendered on the server. After that, we need a mechanism to hydrate it on the client-side with your JS and make it functional.

But do we want all our components to be rendered on the server? If not, then how will we decide what components to render?

Don’t worry, we are getting there!

So which is better among the two?

Depending on our requirements and preferences, we have gone for the later one (isomorphic structure) because we want our document rendering performance to be more consistent, controlled and predictable for our end users.

If you don’t want to get into the complexity involved in setting up and maintaining every bit on your own, you can go for the first one. It will definitely improve the performance than before and is easier and quicker to set up.

Choosing the right library

Now that we have decided to set up and control everything on our own, let’s nitpick the best suitable library as per our requirements:

1) React.lazy and Suspense-

It is one of the most popular names in the React community.

What exactly is React.lazy doing? Is React.lazy creating the bundles for the different components?

In reality, React.lazy is just using our bundler’s code splitting & bundling capacity (be it Webpack, parcel or any other), and providing a syntax on top of it. Basically, it calls the import when it’s getting rendered within the component.

So what does Suspense do?

It is one of the features of React that provides control over the loading state. We can tell Suspense what components it should show while our other component is being loaded.

Is it the one we are looking for? Let’s see!

  • React.lazy supports SSR 😃
  • but Suspense does NOT. 😞

This makes the code-splitting limited to the library.

Can there be a way around it? Yes!

We have to use another library with React.lazy like React.Fragment and mock the Suspense to work like Fragment. Then we load our components synchronously with React.lazy.

import React from ‘react’;
const orgLazy = React.lazy;
React.lazy = importer => orgLazy(syncImport(importer));
React.Suspense = React.Fragment;

It seems too clumsy, right? Can’t there be any other way? 😖

It’s a YES! We can use a library built on top of React.lazy which provides all the features that we require. The next library we are going to discuss does that exactly.

2. @loadable/component

So what is @loadable/component and how is it different from React.lazy and Suspense?

It’s a library built on top of React.lazy. It takes care of asynchronously loading components as well as supports the SSR rendering of components.

Yeah, looks Good! But how does it work? And how can we selectively render components on the Server?

The components we import using loadable are broken down into separate javascript bundles by the module bundler. While writing the import statement along with our component path, we also provide options object setting SSR where you can choose true or false for that particular component.

If the SSR option is true, our component’s UI gets rendered on the server itself and then we can hydrate the functionality on the client-side by executing the injected JavaScript.

import loadable from “@loadable/components“;const MyComponent = loadable(() => 
import(“../my/component/path.js”), {ssr: true});
<MyComponent /> // Just render your component

The work is done! Now, we have a library that works. But, is @loadable/components perfect? Let’s find out.

For most of the use cases, this is good and we can utilise it. This library is providing both code splitting and SSR support. And for us, this should be the one we are looking for.

But what if we wanted to load our component when it actually comes into the viewport?

At 1mg our focus was not just on splitting the components but also, efficiently importing them whenever required. Well, one option could be writing our own wrapper on the top of it with Intersection Observer (browser API) or importing the component based on the scroll height.

The problem with this approach is that we have to take care of the overhead of maintaining it as well as adding polyfill for intersection observers to get complete browser support.

Is there a better way?

Yes! The next library I’m going to talk about takes care of it all.

Let’s dive straight into it!

3. react-loadable-visibility/loadable-components: Best of both worlds.

This library is built on top of @loadable/components and inherits all the features from it. It also takes away the overhead of writing and maintaining the wrapper to dynamically load the components when they actually come to the viewport.

Problem Solved!!

import loadable from “react-loadable-visibility/loadable-components“;const MyComponent = lodable(() => import(“../my/component/path.js”), {ssr: true});<MyComponent />

Just import the loadable from a different library, that’s the only change required here.

So Far, So Good!

Can we use multiple components? If so, why should we do that?

Let’s say, for most of the cases we want to import the components once they are in the viewport. But for some specific components, we want to load them on the custom event such as a drawer menu component on a button click or something similar.

Well, to cater to this, we can definitely use both @loadable/components and react-loadable-visibility/loadable-components as they are supersets of one or the other and they gel up well to give us the control we want on the component loading.

Notable Mentions:

  • Universal-component — They are by far the oldest, and they still maintain the library. They started code splitting with webpack. But, they are not as feature-rich as compared to their younger siblings.
  • React-loadable — A very popular library but not that well maintained. Made code spitting a popular thing. Issues are closed so there is no community around.
  • Imported-component — This could be the go-to library if you are going for module bundlers like ism or parcel.
  • React-async-component — This library supports SSR with custom tree traversal but it is no more functional(yet popular).

Along with these, many others came who couldn’t catch up to the fast-growing speed of webpack, React and their migrations to the latest generations.

TL;DR

  1. All kinds of code splitting libraries provide just a syntax for splitting the components, the actual code splitting is performed by the module bundler.
  2. If you are going for just SPA and CSR, then choosing a library for component splitting shouldn’t be a hitch.
  3. You can use React.lazy for CSR and don’t have to take pain for setting up any other library.
  4. For SSR/Universal/Isomorphic react applications, you can pick a library that can render the component on the server. Unfortunately, Suspense doesn’t work with SSR.
  5. You can go for @loadable/components and react-loadable-visibility/loadable-components for achieving this as they build on top of React.lazy and cater to most of the needs.

I hope you enjoyed this blog!

Do share your feedback and suggestions in the comments section below. Or, if you just want to talk shop feel free to reach out to me on LinkedIn and Twitter.

If you liked this blog, please hit the 👏 . Stay tuned for the next one!

— Coming up next- Implementing @loadable/components

--

--