George Stefanis

Reducing your Webpack bundle size

24 Feb 2018

The last couple of months, my team at Transferwise, has been working to deliver a new feature on our product. We call this feature Money Tracker and helps TransferWise users to track their money better. I've been developing it for the web using Angular in an external component. As a general approach for our frontend all of our new features are created in their own repository, using ES2015, Webpack and Babel.

After deploying the first versions and going through bug fixing we found out that my newly created bundle from Webpack was 1mb. This is unacceptable size for an app as large as ours (or any app really). After a bit of fiddling around I managed to drop the size of it in less than 40kb. This is how I did it.


The first step is to find out why your bundle is that big. The hard way to do this is by searching in the bundle outcome of Webpack for know libraries. But this is tedious and you'll miss stuff for sure. The best way to do so is by using a Webpack analyser. I ended up using Webpack Visualizer by Chris Bateman.

To use it just do npm install --save-dev webpack-visualizer-plugin and then in your webpack.conf.js you need to add the following things:

400: Invalid request

Now if you run your Webpack build job locally you will get a stats.html file. That will look something like this:

Webpack visualizer pie chart

We can see a few things here. The size of my bundle (1mb 😱) and a very nice breakdown where is everything coming from. In my case I was bundling moment.js, bootstrap.css, bootstrap.js and an internal TransferWise library. All of these things for me were peer dependancies, so I shouldn't have bundled them.


This is the point that you need to understand where this files are coming from. There are two suspects here, libraries that you haven't marked as externals in your webpack.conf.js or libraries you have explicitly included in one of your files, e.g import moment from 'moment'. Lets look at these two things.

Webpack externals

This is a configuration in Webpack that tells the bundler that it shouldn't pack these things, because they will be already there to the application that is going to use your new module. This is excellent for these cases where you are developing a library or a component to be used by a bigger app. By using externals you greatly reduce your bundle size.

So in my moment example I should have added the following line in my webpack.conf.js:

400: Invalid request

Now, for bootstrap and the TransferWise library I had already an entry for them in externals, so I really wondered why they were still bundled. And that's how we go to the second point.

Explicit imports

In my package I have central index.js that works as the starting point of everything. Bootstraps the component, imports necessary libraries etc. In there I had these three lines:

400: Invalid request

These three lines were saying to Webpack that it should really bundle them as the application needs them. That wasn't necessary though. These libraries are marked and peer dependancies in my package.json so the app that uses my component should already have them. So I removed them, and they stopped being bundled. It was my bad for including them in the first place.

The only caveat here is that now your tests might be failing (mine did, that's one of the reasons why I put them there in the first place). To solve that just import the necessary libraries in your test configuration file.


So here what I learn:

comments powered by Disqus