Webpack Bundle Analyzer can show you exactly what’s taking up space in your JavaScript bundles, helping you optimize them.

Let’s see it in action. Imagine you’ve got a typical React app. You’ve run npm install react react-dom and maybe added a few other libraries like lodash and moment. You build your app with Webpack, and then you run the analyzer:

npm install --save-dev webpack-bundle-analyzer

Then, in your package.json, add a script:

"scripts": {
  "analyze": "webpack --profile --json > stats.json && webpack-bundle-analyzer stats.json"
}

Running npm run analyze will first generate a stats.json file containing detailed information about your build, and then open a browser window with an interactive treemap visualization.

Here’s what a simplified stats.json might look like (truncated for brevity):

{
  "modules": [
    {
      "id": "./src/index.js",
      "name": "./src/index.js",
      "size": 1024,
      "chunks": [0]
    },
    {
      "id": "node_modules/react/index.js",
      "name": "node_modules/react/index.js",
      "size": 50000,
      "chunks": [0]
    },
    {
      "id": "node_modules/react-dom/index.js",
      "name": "node_modules/react-dom/index.js",
      "size": 60000,
      "chunks": [0]
    },
    {
      "id": "node_modules/lodash/lodash.js",
      "name": "node_modules/lodash/lodash.js",
      "size": 100000,
      "chunks": [0]
    },
    {
      "id": "node_modules/moment/moment.js",
      "name": "node_modules/moment/moment.js",
      "size": 150000,
      "chunks": [0]
    }
  ],
  "chunks": [
    {
      "id": 0,
      "files": ["bundle.js"]
    }
  ]
}

The treemap visualizes this data. Each rectangle represents a module, and its size corresponds to the module’s size in the final bundle. Larger rectangles mean larger modules. You’ll see your application code, but more importantly, you’ll see the dependencies from node_modules.

The problem this solves is the "mystery bloat" in JavaScript bundles. As projects grow and more libraries are added, it’s easy for the bundle size to balloon without anyone knowing exactly why. This leads to slower page load times, especially on mobile or slower networks, and a worse user experience. Webpack Bundle Analyzer directly addresses this by providing a clear, visual breakdown.

Internally, Webpack’s --profile --json flags generate a comprehensive JSON output (the stats.json file) detailing every module, chunk, and their relationships. The webpack-bundle-analyzer tool then parses this JSON. It calculates the total size of each module and its dependencies, aggregates them, and renders them as a treemap using libraries like d3-hierarchy. The treemap algorithm partitions the available space based on the relative sizes of the modules. You can click on any rectangle to see its exact size and the modules it includes.

The exact levers you control are primarily through your Webpack configuration and how you manage your dependencies. For instance, if you see lodash is huge, you might switch to using lodash-es and configure Webpack’s tree-shaking to only include the specific lodash functions you actually use, rather than the entire library. Similarly, if moment.js is a large contributor, you might explore lighter alternatives like date-fns or dayjs, or use Moment’s moment-locales-webpack-plugin to exclude locales you don’t need.

When you generate your stats.json and open the analyzer, you’ll see a breakdown of your entire application. If you’ve used Webpack’s SplitChunksPlugin to create separate vendor chunks, you’ll see those as well, allowing you to analyze the size of your dependencies independently from your application code. This separation is crucial because third-party libraries are often the biggest contributors to bundle size and are also less likely to change frequently, making them ideal candidates for long-term caching.

A common pattern is to see a large, monolithic node_modules entry if you haven’t configured code splitting effectively. The analyzer will highlight this, prompting you to set up SplitChunksPlugin to break out frequently used libraries into their own cached bundles. You can configure SplitChunksPlugin in your webpack.config.js like this:

// webpack.config.js
module.exports = {
  // ... other config
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
      },
    },
  },
};

This configuration tells Webpack to group all modules from node_modules into a separate vendor chunk. The analyzer will then show vendor.js as a distinct, often large, rectangle, allowing you to investigate its contents.

The analyzer also helps identify duplicate dependencies. If you have multiple libraries that depend on different versions of the same package, Webpack’s module resolution might include both, or a merged version, which can still be larger than necessary. The treemap will often show these as separate, smaller rectangles under different parent modules, or a single larger rectangle if they’ve been merged.

The next step after identifying large dependencies is often figuring out how to replace them or configure them more efficiently.

Want structured learning?

Take the full Webpack course →