A script treemapis a visualization that breaks down a JavaScript bundle's contents by size — showing every package, file, and function as a proportional rectangle. The bigger the rectangle, the more bytes that module ships to the browser. It's the fastest way to find which dependencies are eating your bundle budget.
Think of a treemap as a body-composition scan for your bundle. The total weight number tells you something is heavy — the treemap tells you which organ is doing the heaviest lifting and which one is fat that can be cut.
Bundles grow silently. Every new feature pulls a transitive dependency, every refactor leaves a few unused imports, and every "just install it" decision adds bytes the user pays for on every visit. The treemap is the only artifact that lets you see this drift in proportion.
A treemap is a nested rectangle chart. The outer rectangle is the entire bundle. Inside, each child rectangle is a package or file — its area proportional to its byte share. Drill into a package and you see its files. Drill into a file and you see its functions or exports.
Here is what a typical first-look reveals on a Next.js app shipping ~480 KB of JavaScript before optimization:
Total bundle: 482 KB (gzipped: 156 KB)
├── moment.js ......................... 250 KB ← biggest rectangle
│ └── locale/ (all 130 locales) ..... 180 KB (you only use en-US)
├── lodash ............................ 72 KB
│ └── (used 4 functions of 200+) .... (replaceable with lodash-es + tree shaking)
├── core-js polyfills ................. 60 KB ← polyfills for IE11 (your audience: 0%)
├── react + react-dom ................. 44 KB (keep — earns its weight)
├── chart-library-A ................... 28 KB
├── chart-library-B ................... 26 KB ← duplicate-purpose library
├── your app code ..................... 22 KB
└── source maps (.map shipped) ........ 18 KB ← should be excluded from production
Action plan:
moment.js → replace with date-fns (~30 KB) saves 220 KB
core-js → drop legacy polyfills via browserslist saves 60 KB
chart libs → consolidate to one saves 26 KB
source maps → exclude from production deploy saves 18 KB
───────────────────────────────────────────────────────────────────
Projected new total: ~158 KB (gzipped: ~52 KB) ~67% reductionThe treemap doesn't make these decisions for you — it makes them visible. Each finding goes through the same triage: keep, replace with a lighter alternative, or lazy-load on demand.
The mainstream open-source build tooling all has free treemap output. Pick the one that matches your bundler.
// Adding webpack-bundle-analyzer to a Next.js project
// 1. Install
npm install --save-dev @next/bundle-analyzer
// 2. next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// your existing Next.js config
});
// 3. Run it
ANALYZE=true npm run build
// Opens an interactive treemap in your browser showing
// every package, every chunk, and every byte.The largest packages should be ones you consciously chose. If your charting library is bigger than React itself, that's a red flag. Ask: do we use 5% or 50% of this dependency's surface area?
Libraries you didn't install directly often appear because something you did install pulls them in. Run npm ls <package> to see who brought them along, then decide whether to switch or live with it.
core-js polyfills for browsers your audience doesn't use are pure dead weight. Set a strict browserslist target (e.g. "> 0.5%, last 2 versions, not dead, not IE 11") so the bundler stops compiling these in.
// package.json
{
"browserslist": [
"> 0.5%",
"last 2 versions",
"not dead",
"not IE 11"
]
}When two dependencies disagree on the version of a shared package, both versions ship. The treemap surfaces this as two near-identical rectangles. Resolve with npm dedupe or pin a single version via resolutions (yarn) / overrides (npm).
.map files are debugging aids and shouldn't reach end users. If they appear inside the production bundle, fix your build config — most bundlers have a productionSourceMap: false or equivalent.
import _ from 'lodash' ships all of lodash. import debounce from 'lodash/debounce' (or the ESM lodash-es) ships only debounce. Treemaps make this kind of mistake immediately visible.
// Bad — ships ~70 KB
import _ from 'lodash';
const fn = _.debounce(handler, 200);
// Good — ships ~2 KB
import debounce from 'lodash/debounce';
const fn = debounce(handler, 200);If a treemap shows that an admin dashboard or a rarely-used checkout step ships in the main bundle, dynamic-import it instead so 95% of visitors never download it.
// Next.js dynamic import (client-side only)
import dynamic from 'next/dynamic';
const AdminPanel = dynamic(() => import('./AdminPanel'), {
loading: () => <p>Loading...</p>,
ssr: false,
});moment.js is the canonical example: ~250 KB minified, mostly locale data. Replace with date-fns (~30 KB, tree-shakable) or native Intl.DateTimeFormat for most use cases.
What's happening: One dependency takes more than 25% of your total JavaScript — and you're using a small fraction of its surface area.
Fix: Replace with a lighter, tree-shakable alternative (moment.js → date-fns, lodash → lodash-es, axios → native fetch). If replacement isn't feasible, lazy-load it on the routes that need it.
What's happening: core-js or regenerator-runtime ships large polyfills for features modern browsers already implement.
Fix: Tighten browserslist to your real audience and re-run the build. Verify that core-js's rectangle shrinks dramatically afterward.
What's happening: Two dependencies pull in different versions of the same underlying package, and both ship.
Fix: Run npm ls <package> to find the conflict, then pin a single version with overrides (npm) or resolutions (yarn). Re-check the treemap to confirm the duplicate is gone.
What's happening: .map files end up bundled with your production JavaScript, doubling shipped bytes.
Fix: Set productionSourceMap: false (Next.js) or the equivalent in your bundler config. Generate maps separately and upload them to your error-tracking service rather than serving them to users.
Every red flag in a treemap maps to one of three actions. Use this as a quick reference.
| Finding | Typical Size | Best Action | Expected Saving |
|---|---|---|---|
| moment.js with all locales | ~250 KB | Replace with date-fns or Intl | ~220 KB |
| Full lodash via default import | ~70 KB | Per-function imports or lodash-es + tree shaking | ~60 KB |
| core-js polyfills for legacy browsers | 30–80 KB | Tighten browserslist; drop IE11 | 30–80 KB |
| Duplicate package versions | Variable | Dedupe; pin via overrides/resolutions | Equal to the duplicate |
| Admin / rare-route code in main bundle | 20–100 KB | Dynamic import / route-level code split | 20–100 KB for most users |
| Source maps shipped to production | Equal to source size | Disable in production build config | Equal to source size |
A script treemap is a chart that breaks down a JavaScript bundle by size, drawing each package, file, or function as a rectangle proportional to its byte share. It's the standard tool for diagnosing bundle bloat.
Industry analysis (HTTP Archive Web Almanac, 2024) puts the median at 35–40% unused JavaScript at first load. Many sites are well above 50%. A treemap combined with the Coverage tab in Chrome DevTools shows you exactly which files are responsible.
Look at minified, gzipped (or brotli-compressed) sizes — that's what users actually download. Most treemap tools show both; trust the "parsed size" or "gzipped size" column for real-world impact comparisons.
At minimum every quarter, and on every major dependency upgrade. Better: wire it into CI so a build fails when the main bundle grows past a budget (for example 170 KB gzipped per route).
Both. The treemap shows you what's shipped; the Coverage tab in Chrome DevTools shows you what's executed. The gap between them is your unused JavaScript.
Indirectly but meaningfully. Generative search systems preferentially cite pages that already rank well in traditional search, and ranking depends on Core Web Vitals — which JavaScript weight directly affects. A bloated bundle quietly hurts both your search visibility and your odds of being chosen as an AI citation.
A reasonable target for a content-driven site is ≤ 170 KB gzipped JavaScript per route on first load. Single-page apps with rich interaction can justify more, but every additional 100 KB on mobile costs roughly 100 ms of main-thread work.
A script treemap turns "our bundle is too big" from a vague worry into a ranked to-do list. The pattern is always the same: spot the big rectangles, identify which are surprises, polyfills, duplicates, or whole-library imports, and apply one of three fixes — keep, replace, or lazy-load.
Run a Greadme deep scan to see your unused JavaScript, oversized scripts, and render-blocking bundles per page, with an AI-generated fix attached to each finding.