A library to solve React code splitting client-side and server-side.
If you are familiar with React ecosystem (Babel, Webpack, ES6…), you have probably already heard of code splitting. Code splitting means dividing your code into small pieces called “chunks” to reduce the size of bundle loaded by user. Reducing the size of a chunk makes it load and run faster. Amazon said it better “1 sec could cost $1.6 Billion”. Web performance is a feature, not a fix, if you can make your website faster, you should do it.
Webpack has been supporting on-demand code splitting for a while and Webpack 2 has introduced dynamic-import support. This is a new specification currently in stage 3 of TC39 process. Using
import() is the best way to split your code.
An example of basic code splitting:
webpack main.js output.js on the previous example will result in two files: the first,
a.js, the second
Most of the time you are using react-router to manage your React application. — If you are not doing so, your application is probably small and does not require code splitting. — The best way, we found out, to split your application is by route. Why? Because routes can be easily loaded asynchronously without degrading the user’s experience since they expect a little wait after clicking on a link.
If you are using the version 4 of react-router, your code looks like this:
In this example,
Topics components are loaded synchronously. This means that Webpack will not put them in different chunks. The goal is to load them asynchronously using
To do this is to wrap your component then load it on mount:
As you can see it’s very simple but you don’t want to have to write that everywhere. loadable-components can do it for you, this is exactly the goal of
loadable() function. It wraps your components into an async component loaded at mount:
All your routes are now loaded asynchronously and will be splitted by Webpack, pretty easy isn’t it? 😌
This is the simple case, with no loading or error support, if you need it you can refer to the documentation of loadable-components.
At this point you are probably thinking: “I’ve already read it ten times, the problem is not there!”. Yes, you are right, the real problem with code splitting is to make it work with server-side rendering. And providing a solution to this is the goal of loadable-components.
Server-side, you do not have any Webpack. It is node and node does not currently support dynamic imports. You have two choices:
- Building your code with Webpack targetting node
- Transpiling it using babel-plugin-dynamic-import-node
Whatever your choice, it is the same, we just need to make
import() work in a Node.js environment.
Rendering the application server-side
The first problem you will face is rendering your application. React renders an application synchronously, you cannot say “Hey, wait for my component please!”. All components have to be loaded before. The difficulty is that you do not have any idea of what components are required before rendering the application. You have to load modules before rendering the application but you also have to render the application to know what modules are needed.
Apollo is a GraphQL client, it fetches asynchronous data from the server. Server-side they have exactly the same problem. They have to load data before rendering the application but they also have to render the application to know what data are needed. They solved it in an elegant way: traversing the React tree. The solution is pretty simple, we go over all components and collect required modules. If you are curious about how it works, you should take a look at the tree traversing code.
I tried to implement it with the simplest possible API:
getLoadableState traverses the React tree and loads all needed modules before rendering the application.
Load components client-side
At this point it works, but you have two problems:
- Client-side you will see a blink because the client loads modules asynchronously even if the server has rendered it
- There is a mismatch between client and server, the client-side rendered code does not contain asynchronous modules
To resolve this problem, loadable-components does two things:
- It collects modules loaded server-side during the tree traversing
- It transfers these id to the client and loads modules before rendering the application
Again it is easy to implement:
As you can see, it collects a state containing modules ids loaded during tree traversing. Then the state is transferred to the client in a global variable. I got inspired by styled-components for the
getScriptTag and the
Finally, client-side, we have to load components found in the state before rendering our application:
You now have an application working with code-splitting and server-side rendering.
Happy code splitting! I hope you enjoy loadable-components ✂️✨!