Analyze JavaScript With a Script Treemap: Complete Guide (2026)

Saar Twito9 min read
Saar Twito
Saar TwitoFounder & SEO Engineer

Hi, I'm Saar - a software engineer, SEO specialist, and lecturer who loves building tools and teaching tech.

View author profile →

What Is a Script Treemap?

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.

Key Facts (TL;DR)

  • What it is: A nested rectangle chart of a JavaScript bundle, sized by bytes per module.
  • Median unused JS: The median page ships 35–40% unused JavaScript at first load (HTTP Archive Web Almanac, 2024).
  • What you look for: Big rectangles, surprising entries, polyfills, duplicates, and source maps shipped to production.
  • Three actions per finding: keep, replace with a lighter alternative, or lazy-load on demand.
  • Performance link: JavaScript weight directly drives Total Blocking Time, Interaction to Next Paint, and Largest Contentful Paint.
  • Common quick win: Replacing moment.js (~250 KB minified) with date-fns (~30 KB tree-shaken) is a typical 200+ KB cut.

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.

Why Bundle Visualization Matters

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.

  • Unused JavaScript is the norm, not the exception. Industry analysis shows the median page loads roughly 35–40% JavaScript that is never executed during a session. The treemap shows you exactly which files contribute that waste.
  • Bundle size determines the floor of your performance. Even with perfect caching, parse and compile time scales with bytes. On a mid-tier mobile device, every extra 100 KB of JavaScript adds roughly 100 ms of main-thread work.
  • Transitive dependencies hide in plain sight.A library you intentionally added may pull in three more you didn't. The treemap surfaces those uninvited guests.
  • Core Web Vitals depend on it.Heavy JavaScript bundles inflate Interaction to Next Paint (target < 200 ms) and delay Largest Contentful Paint (target < 2.5 s) — both confirmed Google ranking signals.
  • AI search visibility follows performance. Generative answer engines preferentially cite pages that already rank well, and ranking depends on Core Web Vitals.

How to Read a Treemap (Worked Example)

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% reduction

The 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.

How to Generate a Treemap

The mainstream open-source build tooling all has free treemap output. Pick the one that matches your bundler.

  • Greadme deep scan — flags unused JavaScript, oversized scripts, and render-blocking bundles per page, and pairs each finding with an AI-generated fix or a one-click GitHub PR.
  • Greadme crawler scan — runs the same JavaScript audit across every indexable page so you can spot which templates ship the most bloat.
  • webpack-bundle-analyzer — interactive treemap for any webpack-based project (including Next.js).
  • source-map-explorer — works with any project that emits source maps; great for Create React App and custom builds.
  • rollup-plugin-visualizer — treemap output for Rollup and Vite builds.
  • vite-bundle-visualizer — zero-config treemap for Vite projects.
  • Chrome DevTools → Coverage tab — complements a treemap by showing which JavaScript actually executed at runtime, distinguishing "shipped" from "used."
// 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.

What to Look For in a Treemap

1. Big Rectangles — Question Whether They Earn Their Size

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?

2. Surprising Entries — Transitive Dependencies You Didn't Choose

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.

3. Polyfills for Modern Features

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"
  ]
}

4. Duplicate Packages — Multiple Versions of the Same Library

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).

5. Source Maps Shipped in Production

.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.

6. Whole-Library Imports When You Want One Function

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);

7. Heavy Routes That Should Be Code-Split

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,
});

8. Date / Utility Libraries Larger Than Necessary

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.

Common Bundle Problems and Fixes

Problem: A Single Library Dominates the Bundle

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.

Problem: Polyfills for Browsers Nobody Uses

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.

Problem: Duplicate Packages

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.

Problem: Source Maps in Production Bundles

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.

Treemap Findings vs. Action: Decision Matrix

Every red flag in a treemap maps to one of three actions. Use this as a quick reference.

FindingTypical SizeBest ActionExpected Saving
moment.js with all locales~250 KBReplace with date-fns or Intl~220 KB
Full lodash via default import~70 KBPer-function imports or lodash-es + tree shaking~60 KB
core-js polyfills for legacy browsers30–80 KBTighten browserslist; drop IE1130–80 KB
Duplicate package versionsVariableDedupe; pin via overrides/resolutionsEqual to the duplicate
Admin / rare-route code in main bundle20–100 KBDynamic import / route-level code split20–100 KB for most users
Source maps shipped to productionEqual to source sizeDisable in production build configEqual to source size

FAQ

What is a script treemap?

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.

How much unused JavaScript does a typical page ship?

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.

Should I look at minified or unminified sizes?

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.

How often should I run a treemap analysis?

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).

Treemap vs. the Coverage tab — which should I use?

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.

Do AI search engines like ChatGPT and Perplexity care about bundle size?

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.

What should my bundle budget be?

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.

Conclusion

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.