Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Webpack Visualizer

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

You've made it to the end! So let's do something fun!

The cool thing about Webpack is that it allows you to just code: import modules when you need them, import CSS and let Webpack figure out how to package it all together.

The downside is that you might not realize if your built files are becoming too big! Like, maybe you import a huge library... but only use a small part of it. Or, you have some big code that can be code split. There's a fun solution to seeing these optimizations!

Dumping the Stats

Google for Webpack visualizer. The one I like is by someone named Chris Bateman. Hi Chris!

When you execute Webpack, you can tell it to dump a JSON file with very detailed information about the final assets. Find your terminal and stop webpack. Use the full, long version of our production command: NODE_ENV=production ./node_modules/.bin/webpack, and then --json. Save the output to a stats.json file:

NODE_ENV=production ./node_modules/.bin/webpack --json > stats.json

Run it! Webpack compiles like normal... but it outputs JSON, which we save to stats.json. Check out that file: it has details about errors, warnings, and endless information about the assets.

Sweet! Now: back to the browser! Click the box to load our stats.json.

Wow! A donut! It looks delicious! Apparently, 97.6% of our delicious donut... I mean 97.6% of our code is coming from stuff in node_modules/. Only this tiny portion comes from our assets/ directory! As you move out, it describes each part in more and more detail.

Oh, and notice: we're looking at all chunks. But you can also view this graph for only specific JavaScript files. This 02d file is actually our code split chunk!

But let's look at everything. Inside node_modules/, jquery takes up 39%. The second biggest portion... is something called core-js, at 29.5%! That's interesting... more on that in a moment.

Next is bootstrap-sass, sweetalert and then css-loader... which if you look closer is Font Awesome. A few smaller libraries finish off the donut.

Importing core-js Smarter

So... this contained a surprise! The second biggest library is core-js. What the heck is that? And where did it come from?

If you do some digging, you'll find out that this comes from layout.js: specifically from babel-polyfill:

... lines 1 - 5
import 'babel-polyfill';
... lines 7 - 12

Remember, we included this so that it would polyfill the Promise object for older browsers.

I'll hold Command to click into the babel-polyfill module. Huh, this basically just uses another library: core-js. And if you dig further, you'd find out that by requiring the shim module, it imports many polyfills. Yep, we're importing a huge library just to polyfill Promise.

Let's be smarter! First, remove babel-polyfill:

yarn remove babel-polyfill

And now add core-js:

yarn add core-js --dev

While we're waiting, Google for core-js. Ok, this is a library full of polyfills, for ES6, ES7 and other things. Ah, and here are the docs about Promise.

See the "CommonJS entry points" part? This is telling us that if we need the Promise polyfill, we can just require core-js/library/es6/promise or core-js/library/fn/promise.

Let's do that! In layout.js, remove the babel-polyfill line. Instead, import core-js/library/es6/promise:

... lines 1 - 5
import 'core-js/library/es6/promise';
... lines 7 - 12

And now... re-dump our stats:

NODE_ENV=production ./node_modules/.bin/webpack --json > stats.json

When it finishes, find the visualizer and refresh to reset it. Load the new stats.json. Oh, way better! jQuery now takes up 53%... not because it got bigger, but because our JavaScript donut is so much smaller. Then bootstrap-sass & sweetalert2. core-js went from 29.5% to only 8.1%.

Thank you visualizer! So, focus on your code! And then... come to the visualizer to make sure you're not doing anything crazy.

Ok guys! We've done it! We're done! Thank you for staying with me! Webpack is an amazing library... but it's hard. It gives you so much flexibility... which means I have endless ways to confuse myself and mess things up! But now, you are a Webpack power user!

Oh, and also check out Webpack Encore: a library created by Symfony to make configuring Webpack easier, faster and more fool-proof. We'll create a tutorial about it on KnpU soon!

All right, guys. See you next time.

Leave a comment!

Login or Register to join the conversation
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 5 years ago

I tried to run the command with Encore.

NODE_ENV=production ./node_modules/.bin/webpack --json > stats.json

but got error

Error: Encore.setOutputPath() cannot be called yet because the runtime environment doesn't appear to be configured. Make sure you're using the encore executable or call Encore.configureRuntimeEnvironment() first if you're purposely not calling Encore directly.

How can I fix this?

Dmitriy Avatar

I fixed it with a command

yarn run build --json > stats.json


Hey Dmitriy,

Thanks for sharing the solution!


GDIBass Avatar
GDIBass Avatar GDIBass | posted 5 years ago

My membership is up (as of a few minutes ago) but I learned a ton. Thanks a bunch KNPU!


Hey Matt,

Thanks for the kind words! We're happy you like it and our service was useful for you! Please, come back anytime you want ;)


Default user avatar

This chapter made me wonder - are there any ways to... em... tree-shake the polyfill module automatically? Because that looks just like what WebPack do when it tree-shake the javascript.


Hey Ivan!

Yep! And, as far as I can tell (which is a pretty good amount of research), this should happen automatically (as long as you are doing the "import" syntax to get only what you need - e.g. import { Promise } from 'babel-polyfill'. But, this doesn't seem to work reliably. However, in Webpack 4, a lot of improvements have been made - so it may be more reliable there.

But, short answer, yes! But only if you import things correctly, and then... it may not work - so just make sure you watch it closely ;).


Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial explains the concepts of an old version of Webpack using an old version of Symfony. The most important concepts are still the same, but you should expect significant differences in new versions.

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.3.*", // v3.3.16
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.0
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.3
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "^2.0", // v2.1.2
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0" // v3.3.5

What JavaScript libraries does this tutorial use?

// package.json
    "dependencies": [],
    "devDependencies": {
        "babel-core": "^6.25.0", // 6.25.0
        "babel-loader": "^7.1.1", // 7.1.1
        "babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
        "babel-preset-env": "^1.6.0", // 1.6.0
        "bootstrap-sass": "^3.3.7", // 3.3.7
        "clean-webpack-plugin": "^0.1.16", // 0.1.16
        "copy-webpack-plugin": "^4.0.1", // 4.0.1
        "core-js": "^2.4.1", // 2.4.1
        "css-loader": "^0.28.4", // 0.28.4
        "extract-text-webpack-plugin": "^3.0.0", // 3.0.0
        "file-loader": "^0.11.2", // 0.11.2
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.2.1", // 3.2.1
        "lodash": "^4.17.4", // 4.17.4
        "node-sass": "^4.5.3", // 4.5.3
        "resolve-url-loader": "^2.1.0", // 2.1.0
        "sass-loader": "^6.0.6", // 6.0.6
        "style-loader": "^0.18.2", // 0.18.2
        "sweetalert2": "^6.6.6", // 6.6.6
        "webpack": "^3.4.1", // 3.4.1
        "webpack-chunk-hash": "^0.4.0", // 0.4.0
        "webpack-dev-server": "^2.6.1", // 2.6.1
        "webpack-manifest-plugin": "^1.2.1" // 1.2.1