Upgrading to Webpack 2, and Tree-Shaking results

Recently made the upgrade to Webpack 2 because I wanted the tree shaking functionality. Took about a day. Here are some tips, along with the results.

What is Tree-Shaking?

Tree-Shaking (popularized by Rollup) is the elimination of unused exports.

Unfortunately Webpack 2’s Tree Shaking doesn’t seem to eliminate unused exports inside packages — at least in my project (eg. all of Lodash’s functions are included inside my bundle including functions I don’t use).

Upgrading to Webpack 2

Webpack’s official migration guide is fairly comprehensive so I won’t waste time regurgitating what’s already there.

In your .babelrc’s es2015 preset, disable modules


"presets": ["es2015", "react"],


"presets": [
["es2015", {"loose": true, "modules": false}],

This will reduce the file size of your outputted JS.

Ensure that you’re not including the “react-hot-loader/babel” Babel plugin in your .babelrc file!

Tree-shaking won’t work if this plugin is added (wasted a lot of time on this).

Disabling .babelrc and importing your own config in Webpack (OPTIONAL)

Modify the “babel-loader” section in your Webpack config file to look like this (where .babelrc.js is a JS file containing your .babelrc config)

module: {
rules: [
test: /\.js$/,
use: {
loader: 'babel-loader',
options: Object.assign({ babelrc: false }, require('./babelrc.js'))
exclude: /node_modules/,

Ignore the Warning

Now you will get a warning on every Webpack compilation that looks like this:

(node:35513) DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic, see https://github.com/webpack/loader-utils/issues/56
parseQuery() will be replaced with getOptions() in the next major version of loader-utils.

Just ignore it, it’s a problem with one of your Webpack loaders and has nothing to do with you (seems that Webpack will be disabling this warning in a future update).


Webpack 1.13.2

Version: webpack 1.13.2
Time: 25305ms
Asset Size Chunks Chunk Names
js/contact_e752e5854d7a6f7422c9.chunk.js 1.43 kB 4 [emitted] contact
chunk-manifest.json 274 bytes [emitted]
js/catalog_d3fe90090a995c7254ef.chunk.js 41.9 kB 1 [emitted] catalog
js/how_63fabed843b4144effd8.chunk.js 1.35 kB 2 [emitted] how
js/finance_7ae1304d69a4e204bd20.chunk.js 1.56 kB 3 [emitted] finance
js/vendor_876e13b789b38023cf83.bundle.js 394 kB 0 [emitted] vendor
js/about_467405304c50e28a9c63.chunk.js 1.44 kB 5 [emitted] about
js/main_d6bdb9cbe29bc1a164e5.bundle.js 342 kB 6 [emitted] main
css/main_19b822f00587cc2e781c3bbe6bc852f3.css 205 kB 6 [emitted] main
manifest.json 467 bytes [emitted]
sw-toolbox.js 17.4 kB [emitted]
[0] multi vendor 136 bytes {0} [built]
+ 851 hidden modules
Child extract-text-webpack-plugin:
+ 2 hidden modules

Webpack 2.2.1

Version: webpack 2.2.1
Time: 22533ms
Asset Size Chunks Chunk Names
js/about_489704d5cc668862b9be.chunk.js 40.1 kB 0 [emitted] about
js/main_ba108242d5c525cd6d9d.bundle.js 325 kB 1 [emitted] [big] main
chunk-manifest.json 90 bytes [emitted]
js/vendor_a9ebdde44b1b55d21147.bundle.js 396 kB 2 [emitted] [big] vendor
css/main_70a9bee7273fab9ef275864d6bda5aca.css 205 kB 1 [emitted] main
manifest.json 235 bytes [emitted]
sw-toolbox.js 17.4 kB [emitted]
[1] ./~/react/react.js 56 bytes {2} [built]
[18] ./~/react-redux/lib/index.js 475 bytes {2} [built]
[42] ./~/lodash/lodash.js 527 kB {2} [built]
[61] ./~/react-router/lib/index.js 3.62 kB {2} [built]
[72] ./~/react-helmet/lib/Helmet.js 23.4 kB {2} [built]
[89] ./src/modules/analytics/AnalyticsLib.js 1.54 kB {1} [built]
[102] ./~/redux/es/index.js 1.08 kB {2} [built]
[103] ./~/react-dom/index.js 63 bytes {2} [built]
[134] ./~/react-router-scroll/lib/index.js 512 bytes {2} [built]
[206] ./src/client/Device.js 856 bytes {1} [built]
[207] ./~/history/lib/createBrowserHistory.js 3.38 kB {2} [built]
[208] ./~/redux-thunk/lib/index.js 529 bytes {2} [built]
[320] ./src/optimizely/client/index.js 464 bytes {1} [built]
[840] ./src/client.js 3.36 kB {1} [built]
[841] multi babel-polyfill lodash react react-dom react-helmet redux redux-thunk react-redux react-router react-router-scroll 136 bytes {2} [built]
+ 853 hidden modules
Child extract-text-webpack-plugin:
[0] ./~/css-loader/lib/css-base.js 2.19 kB {0} [built]
[1] ./~/base64-js/index.js 3.48 kB {0} [built]
[2] ./~/buffer/index.js 48.6 kB {0} [built]
[3] ./~/ieee754/index.js 2.05 kB {0} [built]
[4] ./~/isarray/index.js 132 bytes {0} [built]
[5] (webpack)/buildin/global.js 509 bytes {0} [built]
[6] ./~/css-loader!./~/postcss-loader!./~/sass-loader/lib/loader.js!./src/style/index.scss 205 kB {0} [built]

main.js went from 342 KB to 325 KB, a 5% decrease in file size. Not exactly drastic, but I’ll take it.

Note that about.js went from 1.44 kB to 40.1 kB and the number of chunks were reduced. This is because apparently the minChunkSizePlugin which had minChunkSize=50000 (50kb) wasn’t working before for whatever reason. The tiny <2kb chunks were eliminated and merged into about.js.

But in case you were curious what the Webpack 2.0 output is with minChunks disabled, here it is:

Version: webpack 2.2.1
Time: 21718ms
Asset Size Chunks Chunk Names
js/main_ba108242d5c525cd6d9d.bundle.js 325 kB 5 [emitted] [big] main
js/catalog_bef2c4168fd2a654ff5c.chunk.js 36 kB 0 [emitted] catalog
js/finance_aa3df57fc02b5cf34182.chunk.js 1.16 kB 2 [emitted] finance
js/contact_d26e17e05dadd81271dc.chunk.js 1.04 kB 3 [emitted] contact
js/about_21fe64e0b5fde043e86d.chunk.js 1.05 kB 4 [emitted] about
js/how_1d3b0d4332d795dd1fa3.chunk.js 968 bytes 1 [emitted] how
chunk-manifest.json 274 bytes [emitted]
js/vendor_a9ebdde44b1b55d21147.bundle.js 396 kB 6 [emitted] [big] vendor
css/main_70a9bee7273fab9ef275864d6bda5aca.css 205 kB 5 [emitted] main
manifest.json 467 bytes [emitted]
sw-toolbox.js 17.4 kB [emitted]

(I’d make a table comparing the file sizes, but Medium doesn’t let you make tables…I know, it’s crazy)

Catalog.js went from to 41.9 KB to 36 KB, a 14% file size reduction

Overall we saw a slight reduction in file size, but nothing to write home about. That being said, our site is very small. If it were larger, then the file size reduction would’ve been more significant.

Was this worth the effort and opportunity cost of upgrading? From a business standpoint, probably not. But at least I got this blog post out of it.

Show your support

Clapping shows how much you appreciated Jeremy Bernier’s story.