Chris Collins

Tools · dev

Bundle analyzer

Drop webpack stats, Vite/Rollup visualizer data, an esbuild metafile or your minified .js with its .map and see what is in the bundle — a zoomable treemap, modules and packages by size, duplicated packages, why each module is included, the chunks, and what changed between two builds.

Added

How to use it

What is making the bundle big, and why is it there? Drop what your bundler already writes about its output and this page answers both, without installing a plugin or uploading anything. It opens with a real Vite build of a small React app; the other examples are the same app built with webpack and esbuild, and a before/after pair where one import pulls in all of lodash.

What to drop

Input How to make it What you get

webpack stats

npx webpack --json > stats.json (webpack 4 or 5; Next.js and CRA builds can write it too). Multi-compiler stats are merged.

Module sizes before minification (“stat”), chunks and the files on disk, the full import graph. Scope-hoisted modules (./src/index.js + 148 modules) are split back into their members.

Rollup / Vite

rollup-plugin-visualizer with template: 'raw-data' (add gzipSize: true, brotliSize: true).

The bytes each module renders to, gzip and brotli per module, the import graph.

esbuild

--metafile=meta.json, or metafile: true in the API (tsup, Bun and others write the same format).

Bytes in the output per module and the input size, the import graph including inputs that were tree-shaken to nothing.

Source maps

The emitted .js files and their .map files (or files with inline maps). Drop several at once.

The real minified bytes per original source, the way source-map-explorer counts them, and a gzip estimate per source. No import graph.

Files can be gzipped (stats.json.gz). Log lines before the JSON (from npm run) are skipped.

How to use it

  • Treemap. Every box is a module, sized by the measure chosen under Size by; boxes are grouped by chunk, then by folder and package. Click a group’s title bar to zoom in, use the trail above to zoom out, and click a module to see why it is in the bundle. Highlight outlines the modules whose path matches (try lodash). Pick a chunk to see only that one.

  • Modules lists them all, sortable by each size, with a filter and a CSV download.

  • Packages totals by npm package and lists duplicated packages: the same package installed in two places (a nested node_modules, or two versions in a pnpm store), how much one copy would save, and which dependency pulls each copy in. Versions show where the path carries them (pnpm, Yarn cache).

  • Chunks shows entry and lazily loaded chunks, the file size on disk, what fills each one, and modules that are copied into more than one chunk.

  • Why included walks the import graph back to an entry and shows the shortest chain, marking import() steps (code that is not in the first download) and require() steps, plus everything that imports the module directly. Modules that were in the graph but tree-shaken to nothing are marked “no bytes”.

  • Compare takes a second build (Load as B): totals, chunks matched by name with their hashes ignored, packages and modules that were added, removed, grew or shrank.

What the numbers mean

The measures differ, so compare like with like:

  • Source (stat) — the module as the bundler read it, after loaders but before minification. It is what webpack reports and what webpack-bundle-analyzer calls “stat”.

  • In output — bytes in the emitted file. From esbuild and source maps this is after minification; from the visualizer it is the module’s rendered code before any later minifier.

  • gzip / brotli — each module compressed on its own. Real files compress better than the sum of their parts because modules share words; the summary shows the whole files' gzip size when the files are loaded. Estimates here use the browser’s CompressionStream.

The source map attribution was checked against source-map-explorer 2.5.3 on webpack, Vite and esbuild output and agrees byte for byte; the test file lists the checks.

For checking version ranges when deduplicating, see the semver tool; to poke around a stats file by hand, the JSON workbench and jq open it too.

Browser support

Any current browser. Gzip estimates need CompressionStream (Chrome, Edge, Firefox 113+, Safari 16.4+); without it the gzip column is left out.

Privacy

Your files are read in the page and never leave the browser. The examples are loaded from this site. Nothing is stored except which example you last opened.

Runs entirely in your browser; nothing you type leaves this page.