Let’s say you’re using webpack to build a web application, and you’ve started to notice that your bundles are getting a bit too huge for comfort.
Finding all the files in your bundle
There are a couple of ways to see what modules or files are contributing to the size of your bundle.
The docs for Create React App offer one suggestion called source-map-explorer
, which is OK. If you’re already generating source maps, you can use source-map-explorer
with zero additional configuration. But I prefer Webpack Bundle Analyzer for a reason that I’ll get to in a bit.
webpack-bundle-analyzer
and source-map-explorer
tell you what modules are getting into your bundle. Sometimes, that’s all you need!
For example, here’s the default output for webpack-bundle-analyzer
for one of my work projects:
In the main App
bundle file (App.107d1f6b.chunk.js
), you can see that the @material/ui
package is a huge chunk, which I would expect because its our primary UI library.
The next biggest module appears to be highcharts
, our chart library. I need that for sure, but do I need to render the charts immediately? Maybe it could be lazy loaded to reduce the size of the initial app bundle?
This sort of view might also help you spot a module that is being included, but not actually used. Like if I didn’t know what highcharts
was, I could do a quick search through my app’s code for require('highcharts
or from 'highcharts'
to see if my code is bringing it in.
Sometimes, though, you need to go deeper.
Finding exactly how a file got in your bundle
Other modules that you’re using will have their own dependencies, so you’ll likely see modules in your bundle that make you think, “how on earth did this get in here?”
Here’s where it’s much more useful to understand how they got there, which the aforementioned tools won’t tell you out of the box.
The Webpack Analyzer site, however, will. But it’s not the most intuitive, and you’ve got to jump through some hoops.
To use it, you need to generate a JSON file with your bundle’s profile/statistics information in a very specific format. A lot of other articles make it sound like this was an obvious and simple thing to do, but I had some trouble nailing it down.
If you’re already using the webpack command line tool (like, your build command is just calling "webpack
" on the command line), you can make the stats file like so:
webpack --profile --json > stats.json
And it’ll output a file stats.json
in the same directory. Done.
If the webpack
command isn’t an option for you, you can use Webpack Bundle Analyzer to generate the file.
First, install it:
# using npm
npm install --save-dev webpack-bundle-analyzer
# or yarn
yarn add -D webpack-bundle-analyzer
Next, add it to your webpack configuration:
// ... other plugins/and such
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
// ... probably other configuration bits
plugins: [
// ...most likely there are other plugins
// add these lines after them
new BundleAnalyzerPlugin({
generateStatsFile: true,
openAnalyzer: false, // <-- set true to open the default analyzer app when the build finishes, if you're interested
}),
],
}
Finally, run your build process again. You’ll see a stats.json
file in the root of your build’s output directory.
Once you have the stats.json
file:
- Go to the Webpack Analyzer site
- If you don’t immediately see "Upload webpack stats", click Open
- Use the JSON file selector to select your new
stats.json
file. - Once everything’s loaded, click Modules
The pretty node-y visualize-y thing up top (a) is not super useful when you have lots of modules and (b) is probably gonna slow your computer to a crawl.
What we’re really interested in is the very large table of files that lists everything in your bundle.
What I like to do here is do an in-browser search for the name of an offending file. When you find it in the table, click the id
column to view the details for that file.
Now here’s the good stuff. Once you’re looking at the details for a module, the reasons block will tell you all the other modules that imported this file. It could be multiple! It could be a different NPM library! You can click the module
column there to go up the dependency tree.
Keep going up and you should eventually find some file you recognize from your project (a good hint is it won’t have node_modules
in the file path), and then you’ll know how the offending file got in there.
When you’re looking at the details for a module, you can also see its full source code and all of its dependencies, which might also be useful for sniffing out how a file got in here.
Now, how to actually chop things out—if it’s even possible—is very dependent on your bundle and the libraries you’re using. Here’s some resources I’ve used before:
- 3 ways to reduce webpack bundle size by Jacob Lind and Possible ways to reduce your webpack bundle size by akintayo shedrack both have a good mix of general principles—like only importing parts of libraries, if possible—and specifics for known-troublesome libraries like
moment
. - If you’re using
lodash
but you notice that another library is usinglodash-es
, you can pretty safely alias'lodash-es': 'lodash'
, becauselodash
andlodash-es
are identical—just packaged slightly differently—and are interchangable, so you don’t need both. - Speaking of
lodash
, it’s ripe for optimizations, but it isn’t always easy.
Hope that helps!