What the JavaScript Bundle Analyzer does
This analyzer reads a JavaScript bundle and its source map in your browser and tells you exactly which packages and files the bundle's bytes came from. It decodes the map's VLQ mappings, charges every generated byte to the source it maps to, groups the files by node_modules package, draws a treemap, finds packages that were bundled more than once from different install paths, and estimates the gzip size with the browser's own compressor.
Nothing is uploaded: the files are read with the File API and decoded in this tab, so private source code and proprietary maps stay on your machine. A webpack stats file also works, with its sizes labelled as pre-minification.
How to use it
- Build your app with source maps enabled (
devtool: "source-map"in webpack,build.sourcemap: truein Vite,--sourcemapin esbuild). - Drop the bundle
.jsand its.maptogether onto the drop zone. For several chunks, analyse the largest one first. - Read the headline size, the gzip estimate and the share taken by third-party packages.
- Use the treemap and package table to find the heavy dependencies, and the findings for duplicated packages.
- Fix one thing - remove, replace, lazy-load or dedupe a package - rebuild and analyse again to confirm the saving.
Reading the results
Sizes are raw bytes of the minified bundle, attributed through the source map. They add up exactly to the file size, with bytes that have no mapping (line breaks, bundler runtime, code from plugins without maps) shown as unmapped.
The gzip figure compresses the whole bundle with the browser's CompressionStream, which uses default gzip settings. Servers using Brotli usually do 15-20% better; the ratio between packages stays similar.
A duplicated package appears under two or more node_modules paths - typically because two dependencies ask for incompatible versions. The "extra" figure is what you would save by keeping only the largest copy.
Without the bundle file, sizes come from the original sources embedded in the map (sourcesContent). They show proportions of source code, not bytes shipped, and the page labels them as an estimate.
Worked example: a shop app with a duplicated lodash-es
The sample bundle is 288,843 bytes. react-dom accounts for 129,000 bytes (about 45%), moment for 60,400 (58,900 plus a 1,500-byte locale), chart-lite for 38,500 and @sentry/browser for 24,800. The app's own code is 19,800 bytes - under 7% of what visitors download.
lodash-es appears twice: once at node_modules/lodash-es (2,900 bytes) and again inside node_modules/chart-lite/node_modules/lodash-es (2,300 bytes). Deduping saves the 2,300-byte second copy; bumping chart-lite or adding an override to align the versions does it.
The bigger win is moment: replacing it with a smaller date library, or loading the date picker that needs it only when opened, removes most of its 60 KB. The browser's gzip brings the bundle to roughly 130 KB on the wire.
Formulas and scoring rules
- Bytes for a segment
bytes = UTF-8 length of generated text from this segment's column to the next segment's column (or the end of the line)- Package
package = the path part after the last node_modules/ (two parts for @scoped packages); otherwise your code- Duplicate waste
extra = package total - bytes of its largest install path- VLQ digit
value = sum of (digit & 31) << 5k; sign = lowest bitBase64 VLQ as defined by the source map specification (ECMA-426).
What to do with each kind of weight
Large libraries used in one place. Load them on demand with a dynamic import() - a chart library needed only on the dashboard, an editor needed only after a click.
Libraries with smaller alternatives. Date, utility and icon libraries are the usual candidates. Check that you import individual functions so the bundler can tree-shake the rest.
Duplicates. Run your package manager's explain command (npm ls <pkg>, pnpm why <pkg>, yarn why <pkg>) to see who asks for which version, then align the ranges or add an override.
Bundle size matters beyond download time: every byte of JavaScript must also be parsed and compiled on the main thread, which competes with user input and shows up as poor INP on slower phones.
Limitations: what the result does not prove
- Attribution is only as good as the source map. Maps from a different build, or bundler plugins that do not emit mappings, put bytes in the wrong place or in unmapped.
- It analyses one bundle at a time. Code shared between chunks is counted in each chunk you analyse.
- The gzip estimate uses the browser's default gzip; your server's compression level or Brotli will give a somewhat different transfer size.
- Stats files report module sizes before minification, so their totals are larger than the shipped bundle.
Privacy: where your data goes
Everything you paste, type or drop is processed in this browser tab. It is not uploaded, logged, stored or sent to analytics. Session recording and tag-manager scripts are switched off on this page.
Standards and sources
- Source Map specification (ECMA-426) - checked 19 Sep 2026
- MDN - CompressionStream
- web.dev - Reduce JavaScript payloads with code splitting
Frequently asked questions
Is my source code uploaded when I analyse a bundle?
No. The bundle and map are read by JavaScript in this tab using the browser's File API, decoded here and compressed here. There is no server request involved, which you can confirm in the DevTools Network panel.
How does a source map tell me what is in my bundle?
Its mappings field records, for positions in the generated code, which original file they came from. Walking every mapped range and adding up its bytes per source file gives an exact breakdown of the bundle by file and package.
Why is the same package in my bundle twice?
Two of your dependencies need versions of it that do not satisfy each other's ranges, so the package manager installs two copies and the bundler includes both. Aligning versions, upgrading one dependency or adding an override usually removes the duplicate.
Does the tool work with Vite, Rollup, esbuild and webpack?
Yes - all of them emit standard version 3 source maps. Index maps with inline sections are supported too. Maps that reference other maps by URL need to be combined first.
Why do some bytes show as unmapped?
Line breaks, the bundler's own runtime and code injected by plugins often have no mapping to a source file. A few per cent is normal; a large unmapped share usually means the map belongs to a different build.
What is a reasonable JavaScript budget?
There is no official figure. Many teams aim to keep the JavaScript needed for the first screen to a few hundred kilobytes compressed and split the rest; measure the effect on INP and LCP on a mid-range phone rather than chasing a number.
Last reviewed by the A2Z.Tools team against the sources listed above.