Bundle's Size: an insight
Tags: react, deploy
Published: 2019-09-24 17:26:23

I don’t know if there are enough articles on how to optimize the bundle’s size, in almost everyone I’ve seen i find something else to add to my pipeline and that is why i want to start with the conclusion I reached.

A bundle is a set of assets grouped based on a dependency analysis deduced from the source code (imports) which solves the problem of integrating the dependencies in a project. Having a range of assets, different in type and functionality, some included explicitly in the code and others implicitly that help the development process (webpack, react, dev mode) it is evident that the complexity is proportional to the size of the bundle .

The final product is not only the content, it also includes the interaction experience and this begins when the user decides to click the link and awaits for the response. Although the bundle simplified the complexity of the dependencies, it is necessary to realize some assumptions to improve this experience, such as that all the code must be present at the same time (splitting), or that must be loaded sequentially (parallelism) and finally that source code must be pretty and understandable (minify).

In summary, i found 4 main areas which group the actions needed to reduce a bundle’s size

*   delete: redundant or sub utilized code
*   extract: assets favoring paralelization
*   divide: group only the code needed
*   optimize: each asset, according to its type

Problem
-------

I made a simple SPA to show some projects and then hosted it on github, I needed to add some dynamic components to the page so I used ReactJS to implement it with [portals](https://jmarroyave-blog.herokuapp.com/en/2019/09/11/reactjs-portals/). My point is why something so simple was so big:
```
app.js 586.6KB
  2.js 377.3KB
  3.js  45.7KB
--------------
     1,009.6KB

polyfill.js  93.1KB
```
![](https://jmarroyave-blog.herokuapp.com/wp-content/uploads/2019/09/jm-git.png)

This is page’s image and it requires almost 1MB of code without counting the HTML, CSS and images. My hypothesis is that “the bundle is packing code that it does not use” so I started to investigate a little and reduce each part involved (although in this post I will focus on the javascript code)

The final result, after reviewing the bundle was:
```
    app.js 481.9KB
    --------------
           481.9KB
    
    polyfill.js  92.9KB
```
which represents a 48% of the original size and although the hypothesis was correct it just describes only a part of the problem.

Analysis and Tools
------------------

To start I needed to see the bundle’s composition

#### webpack

It generates a file with the dependency graph and the assets list, quite big and in json format, has a lot of information but it’s not so manageable
```
webpack --profile --json > stats.json
```
#### [webpack-bundle-analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer)

it analyzes the dependency graph generated by webpack, good for visualizing the composition but not detailed enough

```
    # package
    npm install webpack-bundle-analyzer -g
    
    # analyze, generate report
    webpack-bundle-analyzer stats.json
```

![](https://jmarroyave-blog.herokuapp.com/wp-content/uploads/2019/09/01.png)

#### [source-map-explorer](https://www.npmjs.com/package/source-map-explorer)

very similar to the previous one, not so colorful, but with a better level of detail
```
source-map-explorer script.js
```

![](https://jmarroyave-blog.herokuapp.com/wp-content/uploads/2019/09/source-map-explorer.png)

#### [bundle-stats](https://www.npmjs.com/package/bundle-stats)

provides the most complete list of assets included in the bundle, it is more like a visualization tool for stats.json

![](https://jmarroyave-blog.herokuapp.com/wp-content/uploads/2019/09/bundle-stats.png)

Solution
--------

now, with the bundle’s composition information

![](https://jmarroyave-blog.herokuapp.com/wp-content/uploads/2019/09/01-1.png)

#### 1\. react-dom.development.js

changed the mode to production in webpack.config.js

mode: 'production'
```
all - 2.82MB 
app - 2.58MB 
polyfill - 248.1KB
```
#### 2\. moment.js -> date-fns

The library moment.js, despite of being quite complete is quite big. I replaced it with [date-fns](https://date-fns.org/).
```
all - 2.32MB 
app - 2.08MB 
polyfill - 248.1KB
```
#### 3\. clean unused code

after a quick review of dead code in some components i had to remove some unused imports left
```
all - 2.27MB 
app - 2.02MB 
polyfill - 248.1KB
```
#### 4\. helmet -> document.title

i used helmet just for the page title so i changed it for document.title = “title”
```
all - 2.22MB 
app - 1.98MB 
polyfill - 248.1KB
```
#### 5\. axios -> fetch

I used axios just for server requests and that could be easily replaced by fetch
```
all - 2.03MB 
app - 1.79MB 
polyfill - 248.1KB
```
#### 6\. lint fixes
```
all - 2.03MB 
app - 1.79MB 
polyfill - 248.1KB
```
#### 7\. removing javascript-time-ago

I am working on a framework that I have been building over time and at some point in time I used this functionality which can be replaced by date-fns too
```
all - 1.62MB 
app - 1.38MB 
polyfill - 248.1KB
```
#### 8\. material-ui

it would be a highly cost refactor, I just upgrade the package to the latest version hoping that the devs behind the library were also doing their thing in this matter

#### 9\. react -> preact

changing react for preact? it sounds good although the migration process was a little buggy
```
all - 1.51MB 
app - 1.27MB 
polyfill - 248.1KB
```
#### 10\. remove hot loader and development dependencies

#### 11\. extracting assets: css, fonts, images

webpack - mini-css-extract-plugin
```
all - 1.43MB 
app - 1.19MB 
polyfill - 248.1KB
```
#### 12\. dynamic loading
```
const { FixedSizeList } from 'react-window'; 

const { FixedSizeList } = Loadable({
   loader: () => import('react-window'),
       loading: Loading,
}); 

const FixedSizeList = Loadable({
   loader: () => import('react-window/FixedSizeList'),
       loading: Loading,
});
```
#### 13\. targeting
```
devtool: false,
target: "web", 
externals: {
 React: 'react'
}
```
#### 14\. minimize

Terser

![](https://jmarroyave-blog.herokuapp.com/wp-content/uploads/2019/09/13.png)

Summarizing, I categorized the previous 14 points as follows

#### delete

development support

*   react-dom.development.js
*   removing hot loader

refactoring

*   moment.js
*   helmet
*   axios
*   javascript-time-ago
*   material-ui
*   react

code review

*   unused code
*   linting

#### extract

css, images, fonts

#### divide

Dynamic loading

*   react-window
*   optimizations chunks

#### optimize

targeting and minimizing

This far the list for today, I am aware that it can be more extensive. I would like to know which other points you recommend taking into account.

References
----------

*   [https://lemoncode.net/lemoncode-blog/2018/5/7/webpack-poniendo-a-dieta-tus-bundles-ii](https://lemoncode.net/lemoncode-blog/2018/5/7/webpack-poniendo-a-dieta-tus-bundles-ii)
*   [https://dev.to/sheddy\_nathan/possible-ways-to-reduce-your-webpack-bundle-size-js-secrets-550](https://dev.to/sheddy_nathan/possible-ways-to-reduce-your-webpack-bundle-size-js-secrets-550)
*   [https://medium.com/@poshakajay/heres-how-i-reduced-my-bundle-size-by-90-2e14c8a11c11](https://medium.com/@poshakajay/heres-how-i-reduced-my-bundle-size-by-90-2e14c8a11c11)
*   [https://material-ui.com/guides/minimizing-bundle-size/](https://material-ui.com/guides/minimizing-bundle-size/)
*   [https://goenning.net/2018/11/26/how-we-reduced-initial-jscss-size/](https://goenning.net/2018/11/26/how-we-reduced-initial-jscss-size/)
*   [https://www.intercom.com/blog/reducing-intercom-messenger-bundle-size/](https://www.intercom.com/blog/reducing-intercom-messenger-bundle-size/)