Optimising your application bundle size with webpack

I recently built a React.js application that can be rated as a moderately complex single page application. I was looking to optimise the page load time for various clients and tried profiling the site on one of the available tools to check how we were doing.

Came across testmysite from Google and profiled the webpage and got the following results:

The site was rated as being very responsive design-wise but literally not very “responsive” with the load time. I was evaluating various parts of the code to address the page load time and this post serves as a guide for those looking to optimise the same.

The first step to check is the current bundle size and review if all the dependent packages are absolutely essential.

$ webpack -p --progress
Hash: dbce3735c9520e2dc682
Version: webpack 1.14.0
Time: 54264ms
Asset Size Chunks Chunk Names
dist/index.js 3.29 MB 0 [emitted] main
dist/index.js.map 13.7 MB 0 [emitted] main
[0] multi main 40 bytes {0} [built]
+ 1374 hidden modules

The current bundle size is more than 3 megabytes and that explains the abysmal scores by the profiling tool.

1. Analyse your bundle dependencies

I used webpack-bundle-analyzer which analyses the webpack statistics and gives a great visualization to know the leaky buckets at first glance!

For quick reference, I have listed the installation steps. Install the package using the following command

$ npm install --save-dev webpack-bundle-analyzer

Modify your webpack.config.js to include the plugin. If you have multiple profiles for each environment, better to do this on the production configuration and build the app for that profile

var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// ...
plugins: [new BundleAnalyzerPlugin()]
// ...

Now starting the server should show up the visualization at http://localhost:8888. You should see something like below and you can drill to any level to know the packages that need to be trimmed or removed.

webpack-bundle-analyzer sample report

This should help understand the heavy weight dependencies. Also when importing dependencies, it is better to import only the necessary function/component from the library and this will be packed more effectively in the bundle. The second method as illustrated below is more efficient.

2. The right node environment in the build

Indicating the right environment to webpack will ensure the development and test artefacts are not packed as part of the bundle. To do that, you can define the NODE_ENV as production in the plugins config

plugins: [...
'process.env.NODE_ENV': '"production"'

3. Include minimal source map information

When you build for production, along with minifying and combining your JavaScript files, you generate a source map which holds information about your original files. The source map with maximum detail level can be generated for development or staging environments. But, we can opt for the most optimised form of source maps for production.

Check out your webpack configuration and it is common to see devtool: ‘eval’ which is one of the fastest ways to build apps with most of the development code information bundled in the output. Let’s toggle that setting to be eval-source-map or cheap-module-source-map. Refer this documentation to know more details about each of these configuration params.

Let’s check the bundle size again at this stage to see how much we have saved.

$ webpack -p --progress
Hash: 68a52fddbcc2898a5899
Version: webpack 1.14.0
Time: 29757ms
Asset Size Chunks Chunk Names
dist/index.js 1.71 MB 0 [emitted] main
dist/index.js.map 464 bytes 0 [emitted] main
[0] multi main 40 bytes {0} [built]
+ 1365 hidden modules

From 3.29mb we are down to 1.71mb and also note that the source map file size has reduced considerably!

4. Other notable plugins to optimize bundle size

I am listing the other plugins that I used to reduce the size further. Evaluate the ones appropriate for your use case and use them as needed:

  • compression-webpack-plugin: a plugin for compressing assets that will generate the .gz files along with the assets and depending on the client the asset server can be configured to serve as needed.
  • dedupe-plugin: searches for equal or similar files and de-duplicates them in the output. This comes with some overhead for the entry chunk, but can reduce file size effectively. [Edit: Available by default in Webpack v.2]
  • uglifyjs-plugin: minimizes all JavaScript output of chunks. Refer to documentation for the plethora of options that this plugin deals with
  • ignore-plugin: handy when you want to import selective modules from a library. For e.g., when using moment.js, it is not necessary to include all the locales that come with the library, but only the ones that are needed. This is illustrated in the code example below as well.

The final webpack configuration might look like this:

Let’s check the bundle size again with the final configuration in place…

$ webpack -p --progress
Hash: 68a52fddbcc2898a5899
Version: webpack 1.14.0
Time: 29757ms
Asset Size Chunks Chunk Names
dist/index.js 1.54 MB 0 [emitted] main
dist/index.js.gz 390 KB 0 [emitted] main
dist/index.js.map 464 bytes 0 [emitted] main
[0] multi main 40 bytes {0} [built]
+ 1365 hidden modules

Note the size of the compressed js file and it is down to 390kb This should greatly improve the page load time. Let’s run the google profiler test one more time and check how the site fares with the latest bundle.

Final scores after applying all optimizations


5. Code Splitting

I would like to add code splitting to this post as another important step towards an optimized webpack build. The idea is pretty simple. If you use something like react-router to define multiple pages in your app, it is easy to draw a dependency tree with the components defined in the route as a starting point and bundling all the assets (js,css,images) required for that page as a separate chunk. If you use dynamic imports, discussed here, webpack can smartly split out the chunks of code required by each page.

Follow the steps in this well written article to implement code splitting for your project.

After you have made the changes, when you fire the webpack prod build, you should see an output like this that creates multiple chunks of scripts to download for each page the user visits, thus significantly reducing the page load time!

If you liked this post, please follow me on twitter for more updates on similar topics..

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by RC

Topics of interest

More Related Stories