Swizec Teller - a geek with a hatswizec.com

    How We Used Webpack to Reduce Our JS Footprint by 50

    In this article, I'm going to show you how we went from 30 requests for 3.1MB of minified uncompressed JavaScript to 19 requests for 2.2MB of minified uncompressed JS. Why uncompressed? Because Rails doesn't gzip on localhost, and our production servers are running the new setup already.

    We weren't doing anything stupid before. Tree shaking, minification, code splitting to avoid JavaScript users don't need… we had all of that. We even split our frontend into 5 or 6 discreet apps, each with its own internal code splitting.

    And yet, 30 requests for 3.1MB.

    Our problem: Third-party libraries. There's a bunch of stuff that we need, like Backbone and Handlebars and Lodash and jQuery and so on. Most of them we loaded from public CDN. Some of them we bundled locally into a vendor.js file; this is where the huge size came from.

    You see, when all your apps share a vendor.js file, and some of them are React and some are Backbone, guess what happens? All apps load both React and Backbone.

    We achieved this vendor/app split following Webpack's official guide on code splitting libraries. It suggests using the CommonChunksPlugin to extract common code into a top-level file.

    plugins: [
    // Avoid publishing files when compilation failed:
    new webpack.NoEmitOnErrorsPlugin(),
    new ExtractTextPlugin(
    ? "[name]_style.css"
    : "[name]_style.[chunkhash].css"
    new webpack.optimize.CommonsChunkPlugin({
    names: ["vendor", "manifest"],

    manifest.js is meant to contain Webpack's runtime, and vendor.js contains your more stable third-party dependencies. This is meant to improve caching. shrug

    On top of that, we had a bunch of global libraries configured as externals and loaded them in top-level <script> tags. Not a bad approach per se, but something like the AWS SDK is almost 500kb of compressed JavaScript. When you're using only one function… yeah, no bueno.

    We fixed this situation with a 2-pronged approach:

    • bundle and tree shake all our dependencies ourselves
    • create a different vendor file for each app (entry file)

    Here's how that looks:

    const Apps = {
    // list entries
    // will be reused as Webpack's entry config
    // previously loaded as externals
    const GlobalModules = _.map(
    jquery: ["$", "jQuery", "window.jQuery"],
    lodash: ["_"],
    backbone: ["Backbone"],
    "backbone-validation": ["Backbone.Validation"],
    "raven-js": ["Raven"],
    moment: ["moment"],
    string: ["string", "S"],
    async: ["async"],
    (vars, module) =>
    new webpack.ProvidePlugin(_.fromPairs(vars.map((v) => [v, module])))
    // updated plugins config
    plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new ExtractTextPlugin(
    ? "[name]_style.css"
    : "[name]_style.[chunkhash].css"
    (app) =>
    new webpack.optimize.CommonsChunkPlugin({
    name: `${app}_vendor`,
    chunks: [app],
    minChunks: isVendor,
    new webpack.optimize.CommonsChunkPlugin({
    name: "manifest",
    chunks: Object.keys(Apps).map((n) => `${n}_vendor`),
    minChunks: (module, count) => {
    return count >= Object.keys(Apps).length && isVendor(module);

    All our entry files, or apps as we call them, go in Apps. This helps us iterate over them when building the plugins config.

    What used to be externals loaded in global script tags become GlobalModules. Each is translated into a ProvidePlugin configuration, which essentially replaces all occurrences of $ with require('jquery'), moment with require('moment'), and so on.

    With this approach, we don't have to change any code that relies on global libs' availability.

    Armed with these vars, we dynamically generate a list of Webpack plugins. Each app gets its own vendor file – _vendor.js – and at the end, all files together get a common manifest file with the Webpack runtime. Again, to prevent cache churning.

    Oh, and there's the helpful isVendor function, which I got from Juho's SurviveJS - Webpack book. You should buy it. It's great.

    function isVendor(module, count) {
    const userRequest = module.userRequest;
    return userRequest && userRequest.indexOf("node_modules") >= 0;

    This function tells us if a specific module is a third-party library or our own code. It's third party if it's in node_modules, and our own code if it's not.

    I hope this helps make your website faster. It got our page speed score from 54 to 95 ?

    PS: This article is a spiritual successor to Migrating to Webpack 2: some tips and gotchas.

    Did you enjoy this article?

    Published on February 10th, 2017 in Front End, Technical, Webpack

    Learned something new?
    Want to become an expert?

    Here's how it works 👇

    Leave your email and I'll send you thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

    Join Swizec's Newsletter

    And get thoughtful letters 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"

    ~ Ashish Kumar

    Join over 14,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. ✌️

    Have a burning question that you think I can answer? I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

    Ready to Stop copy pasting D3 examples and create data visualizations of your own?  Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

    Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

    Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first 💰 on the side with ServerlessReact.Dev

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created bySwizecwith ❤️