Put Your Webpack Bundle On A Diet - Part 1

Do you like to wait? Of course you don’t—and neither do your users. This four-part series will show you how to reduce your load time by putting your webpack on a diet.

Slimming down your payload has a number of benefits, including:

  • Your site will load quicker
  • Javascript parsing and execution will be faster (important for mobile apps)
  • Easier on the user's mobile data plan

Initial Analysis

To get started, you will need to generate a log file using webpack and the --json parameter. You should also include the --profile parameter to add timing information, and the --progress parameter to display the status of the build process.

In case you’ve previously modified the webpack stats setting, make sure it’s set to detailed or verbose.

We’ll cover how to further optimize each parameter later on. So for now, the full command will look like so:

webpack -p --json --progress --profile > webpack-build-log.json

For an ad-hoc solution to get the history of log files, you can run the following:

webpack -p --json --progress --profile > "webpack-build-log.`date +%Y-%m-%d_%H-%M-%S`.json"

Note: Is your JSON valid? To make sure the build process does not output any unwanted information, you can use babel-preset-env and its debug parameter.

We have generated a log file! What’s next?

Now that we have a log file, we can pass it to various services that will, in turn, provide you with aggregated information about your build process. One of those services is the official analysis tool.

This online version supports drag and drop, but don’t worry—the log file itself will not leave your machine. You can also find more tools in the official docs.

Our tool of choice: webpack-bundle-analyzer

For bundle optimization, webpack-bundle-analyzer happens to be the most useful. It is also widely used within the community.

You can use the webpack-bundle-analyzer as a standalone command line tool, or as a plugin within your webpack config. The tool will display a dependency graph using a treeview, visualizing the relative sizes of every file in your bundle.

Note: Make sure to pass the directory of your generated bundle as second parameter to get the real, parsed, and gzipped size of every file.

The file-upload-example app in all its unoptimized glory looks like this in the bundle-analyzer:

webpack-bundle-analyzer webpack-build-log.json dist/app

And will result in a diagram similar to this:

Put your webpack on a diet part 1 - image1

Our (intentionally unoptimized) app has a whopping 1.69MB stat size. The app size is 1.76MB parsed and 399.17KB gzipped. Lighthouse says it would take ~3200 ms to the first meaningful paint on a bad 3G connection.

This is far from good, but it’s the perfect starting point for what we’re about to do!

A quick look reveals what this bundle is made of:

  • A lot of comments and whitespace because we “accidently” skipped minification
  • moment.js with all it's locales: 453.34KB pure code / 90.28KB gzipped
  • contentful-management as single bundle: 313.08KB pure code / 57.26KB gzipped
  • babel-polyfill with: 225.91KB pure code / 48.92KB gzipped
  • Also, we can spot multiple occurrences of lodash in the bundle

Let’s continue and get this monster into shape with our healthy webpack bundle diet.

The in-house way of putting your bundle on a diet:

First of all, I want to highlight that webpack has improved greatly. Version 2, and the fairly new version 3, both improved their bundling abilities. Some people say it’s a 50% reduction of the original build time when using version 3.

The webpack project is also backed by many companies, and the fact that some brilliant people now have time allocated by their employer to improve it—new versions get released almost daily.

The fastest way to reduce the payload is to run webpack with the -p switch. This will enable the most basic production settings:

webpack -p

The -p switch alone significantly reduced the bundle size. This will have a roughly 30% improvement on the first meaningful paint on a low-end mobile phone.

Put your webpack on a diet part 1 - image2

Webpack comparison Stat size Parsed size Gzipped size Put your webpack on a diet part 1 - lighthouse First paint on 3G and low-end mobile
intentionally unoptimized 1.69MB 1.76MB 399.17KB ~ 3292 ms
webpack -p 1.65MB 640.45KB 208.79KB ~ 2276 ms

Running webpack with the -p switch will carry out the following tasks:

  1. Minification using the UglifyJsPlugin
  2. Runs the LoaderOptionsPlugin (see its documentation)
  3. Sets the Node.js environment variable to production triggering certain packages to compile differently

Sometimes, executing webpack using only the -p switch works well enough. However, it is good to know that the -p switch does not optimize the following:

  • The Node.js environment variable will not be set to production in webpack.config.js. If you rely on this setting in your config, you still have to prepend the webpack command with NODE_ENV=production
  • CSS created by css-loader is not minimized by default, so you probably want that in production
  • Extracting css into its own file is more effective and caches better. You can use the extract-text plugin for that.
  • Finding the correct source map setting for your needs is essential. We use the quick cheap-eval-source-map in development, and false for no source map at all in production.

Summary

We have barely scratched the surface of what you can do to bring down your bundle size. By just running webpack the -p flag, we have managed to drastically reduce our bundle payload. In our next post, we will run a production-like build to achieve an even better bundle size reduction.

Remember, lessening the payload has a number of advantages. By keeping things small, you decrease the risk of losing customers over a slow user experience—especially when they are using budget hardware through a less than ideal internet connection.

Blog posts in your inbox

Subscribe to receive most important updates. We send emails once a month.