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:
- The
gatsby-plugin-webpack-bundle-analyser-v2
report will be auto opened in your default browser - A report by
gatsby-plugin-perf-budgets
is generated in yourpublic
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.
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.