blog.jakoblind.no

How to use the webpack bundle analyzer

webpack bundle analyzer is an important tool to keep your webpack bundle small. This article describes how to set it up and what to look for in the report generated.

What is webpack bundle analyzer? And when do you need it?

With webpack-bundle-analyzer, you get visualizations of what’s in your webpack bundle. When you see what’s in your bundle, you can optimize it to make it smaller.

First, we are going to install it on an existing project (or you can jump straight into learn how to interpret the results).

How to configure webpack bundle analyzer in a webpack project

(are you using a React framework such as Gatsby, next or CRA then scroll down)

There are two ways to configure webpack bundle analyzer in a webpack project. Either as a plugin or using the command-line interface. Which one you should choose depends on your workflow:

Do you want a report to be created every time you build? Then you should use the plugin

Do you want a separate command for creating the report on-demand? Then you should use the CLI way

How to configure the webpack bundle analyzer plugin

I’ll show you step-by-step how to add webpack bundle analyzer to your existing project. You can also download this complete configuration here on createapp.dev

First, you need to install the dependency as a dev dependency:

# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer

Next, you’ll edit your webpack.config.js

const BundleAnalyzerPlugin =
  require("webpack-bundle-analyzer").BundleAnalyzerPlugin

module.exports = {
  // your webpack config is here
  plugins: [new BundleAnalyzerPlugin()],
}

Every time you run webpack you get a report generated in your output folder. If you look closely at the output it says where the report is placed:

$ npm run build-prod

> empty-project@1.0.0 build-prod
> webpack --mode production

Webpack Bundle Analyzer saved report to /Users/jakoblind/dev/test-webpack-bundle-analyzer/dist/report.html

In my case, it’s in dist/report.html. The report is a regular HTML file that you can open in your browser.

How to configure webpack bundle analyzer CLI

In my experience, I only want the report generated occasionally. I want to analyze my bundle if I suspect the bundle is too big because I have recently added new dependencies, added a new feature, or made big refactorings. So I want a custom command to only generate the report. This is how I do it.

There are two things you need to do. First, generate a stats.json file. And secondly, run webpack-bundle-analyzer with the generated stats.json file from the first step.

You generate the stats.json file like this.

$ npx webpack --profile --json > stats.json

We use npx to run which is included in node 5 and up. This command runs a node module from the command line.

If you are curious about how your stats.json file looks then you can open it up in a text editor. There is a lot of data in there 🙂

Next, you’ll run webpack-bundle-analyzer with the generated stats.json file:

$ npx webpack-bundle-analyzer ./stats.json

Remember to change the path to the path of your stats.json file.

If you get an error here then open up the stats.json file and make sure this is only JSON there. For example, if you get an error like this:

SyntaxError: Unexpected token W in JSON at position 0

It could mean you have configured webpack bundle analyzer as a plugin and your stats.json file starts with this text: Webpack Bundle Analyzer saved report to /Users/jakoblind/dev/test-webpack-bundle-analyzer/dist/report.html

In that case, disable the plugin and/or remove the text above from your stats.json file.

You can also add these two commands as a script in your package.json file so you only have to run one command to generate the report. Edit your package.json file and add the following:

{
	// ...
  "scripts": {
    "report": "webpack --profile --json > stats.json; webpack-bundle-analyzer ./stats.json",
		// your other scripts here...
  },
}

Now you can generate your report with one command:

$ npm run report

webpack-bundle-analyzer with Typescript config file

Do you have configured your webpack config to use Typescript? (That means if webpack.config.ts file. Notice the ts extension) Then you can install types for webpack-bundle-analyzer as well. Do it like this:

npm install --save @types/webpack-bundle-analyzer

Should you analyze the production or dev bundle?

It makes the most sense to analyze the production bundle. This is what you serve to your users. Therefore it’s essential to build and run webpack-bundle-analyzer in production mode. To run webpack in production mode production mode, you pass in the mode flag like this

webpack --mode production

Webpack bundle analyzer react

Are you using React and a framework like Nextjs, Gatsby, or create-react-app for building your app? These frameworks use webpack under the hood and you can get the bundle analyzed. However, the config for these toolkits looks a bit different than the pure webpack config we looked into earlier.

Webpack bundle analyzer with nextjs

With Nextjs you cannot use webpack bundle analyzer out of the box. Instead, you use another tool called next/bundle-analyzer

First, install the dependency to your project.

# NPM
npm install @next/bundle-analyzer
# Yarn
yarn add @next/bundle-analyzer

Next you are going to create a next.config.js file (if you already have it, just edit it) and add the following:

const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true",
})
module.exports = withBundleAnalyzer({})
// if you already have a config here replace {}
// with your config in the line above

Now you can generate a report by setting the environment variable ANALYZE to true before building. Like this:

ANALYZE=true npm run build

When you do, your report will automatically open. You’ll get two reports: one for the server-side and one for the client-side.

Webpack bundle analyzer with gatsby

There are two plugins that use webpack bundle analyzer under the hood. The first one is gatsby-plugin-webpack-bundle-analyser-v2

It’s quite straightforward to install. First, install the dependencies:

# NPM
npm install --save-dev gatsby-plugin-webpack-bundle-analyser-v2
# Yarn
yarn add -D gatsby-plugin-webpack-bundle-analyser-v2

Then add the plugin to gatsby-config.js

/* gatsby-config.js */

module.exports = {
  plugins: ["gatsby-plugin-webpack-bundle-analyser-v2"],
}

Now when you create a Gatsby build with gatsby build then it will automatically open a report in your browser.

This is good, but it doesn’t give you a list of bundles that get loaded on each individual page. However, the next plugin does.

Let’s look into the other plugin named gatsby-plugin-perf-budgets that will give you this detailed information. This is an experimental plugin and it’s used in conjunction with gatsby-plugin-webpack-bundle-analyser-v2 that we just looked at. You install the dependencies for both plugins like this:

# NPM
npm install --save-dev gatsby-plugin-webpack-bundle-analyser-v2
npm install --save-dev gatsby-plugin-perf-budgets
# Yarn
yarn add -D gatsby-plugin-webpack-bundle-analyser-v2
yarn add -D gatsby-plugin-perf-budgets

And then add both of them to gatsby-config.js

/* gatsby-config.js */

module.exports = {
  plugins: [
    "gatsby-plugin-perf-budgets",
    "gatsby-plugin-webpack-bundle-analyser-v2",
  ],
}

Now when you run gatsby build you’ll get two reports:

  1. The gatsby-plugin-webpack-bundle-analyser-v2 report will be auto opened in your default browser
  2. A report by gatsby-plugin-perf-budgets is generated in your public folder with the filename _report.html

In the _report.html file, you will get an overview of all your generated pages. You can click on one page and see the visualization for only that specific page. Pretty nice.

webpack bundle analyzer with create-react-app

First, install the npm dependency

# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer

Then create a new file analyze.js with the following content

const webpack = require("webpack")
const BundleAnalyzerPlugin =
  require("webpack-bundle-analyzer").BundleAnalyzerPlugin
const webpackConfigProd = require("react-scripts/config/webpack.config")(
  "production"
)

webpackConfigProd.plugins.push(new BundleAnalyzerPlugin())

webpack(webpackConfigProd, (err, stats) => {
  if (err || stats.hasErrors()) {
    console.error(err)
  }
})

Run it like this:

NODE_ENV=production node analyze.js

The report will automatically open in your browser.

What should I look for in the reports?

Now let’s look at what’s inside the report. This is how a report of my blog that you reading right now looks like:

Each JS file that is generated has its own big box with a unique color. This is the box that represents the framework files in my blog:

The size of the box corresponds to the size of the JS file. On my blog, the framework files that we see above are the biggest bundle.

The code I have written is in the small blue boxes to the bottom right. Everything else is dependencies and dependencies to the dependencies. That means the heavy part of my bundle is dependencies and there is most room for improvements there.

If you hover over a box you see the exact size (also gzipped).

In each of these larger boxes, there are smaller boxes. Each one represents modules. You can also hover over individual modules to get their size.

You can zoom in and out with the scroll wheel. You can also click a box to zoom in on it.

Now you know what is in the report and how to navigate the report, let’s see what you can do with the information.

Do you see some large libs? they might be tree shakable, for example lodash

If you see some large libraries in there, look into if they are (tree-shakable). One common example of this is lodash. With tree shaking, you don’t have to bundle the whole library, but only the part you use.

This is how it looks if you include lodash without tree-shaking it: webpack-bundle-analyzer-lodash.png

Sometimes you can see lodash in the webpack bundle analyzer, but you don’t have lodash in your package.json file. What to do then? Then most likely one of your dependencies has lodash as a dependency. The thing you can do in that case is to find which dependency has lodash as dependency. You can look at the package-lock.json. There you’ll see a detailed report of every dependency of your project.

When you have identified the guilty dependency, there is unfortunately not much you can do. You can consider stop using it and replace it with something different. Or send a PR to them improving the usage of lodash.

Are you depending on deprecated libs (for example moment?)

The best way to reduce the bundle size is if you can remove something completely. If you see momentjs in your report, then this might be possible. The team discourage Momentjs from being used in new projects and they have stopped develop new features for it. Instead they have some recommendations. And the recommendation that has least impact on your bundle is using no library at all. This is fully possible in modern JavaScript. Read more in the article You Probably Don’t Need Moment.js Anymore

There might be similar cases. Maybe you use a heavy dependency for something really simple that you can code yourself?

Do you see some large bundles in the report?

Webpack recommends that a bundle should be a maximum 250kb unminified. If you see larger bundles than that, use code splitting to split them up and maybe lazy load some of them

Do you see any duplications?

Do you see the code duplicated in many bundles? Maybe you can look into splitting out that code into its own bundle.


tags: