Bundle Analyzer Guide: Identifying and Fixing JavaScript Bloat

#1Bundle analyzer: finding and fixing JavaScript bloat
What we tested: We evaluated the tools and techniques described in this article using real developer workflows. All browser-based tools on this site run locally, your data never leaves your machine.
JavaScript bloat hurts in the same few places every time: download time, parse time, and main-thread work.
A bundle analyzer helps you see where the weight is coming from so you can cut the dependencies, code paths, or imports that do not belong in the client bundle.
#21. What Is a Bundle Analyzer & How Does It Work?
Modern JavaScript frameworks (React, Next.js, Vue, Svelte, Angular) use bundlers, such as Webpack, Vite, esbuild, Rollup, or Rspack, to transform hundreds of TypeScript/JavaScript modules and node_modules dependencies into optimized production .js chunks.
During compilation, a bundle analyzer hooks into the build pipeline to calculate:
- Stat Size: The raw size of source code before minification or transformations.
- Parsed Size: The actual size of the minified JavaScript code generated for the browser.
- Gzipped / Brotli Size: The compressed payload size transferred over the network.
#3The Treemap Visualization
The output of a bundle analyzer is a Treemap Chart:
┌─────────────────────────────────────────────────────────────┐
│ PRODUCTION BUNDLE │
├───────────────────────────────┬──────────────┬──────────────┤
│ │ │ chart.js │
│ moment.js │ lodash-es │ (160 KB) │
│ (280 KB) │ (70 KB) ├──────────────┤
│ │ │ aws-sdk │
│ │ │ (120 KB) │
├───────────────────────────────┴──────────────┴──────────────┤
│ app.tsx (45 KB) | components/ (30 KB) | utils/ (15 KB) │
└─────────────────────────────────────────────────────────────┘In a treemap:
- Area is proportional to file size: larger rectangles represent heavier dependencies.
- Color grouping categorizes packages by folder or chunk name.
- Nested blocks show sub-modules inside larger libraries.
Looking at a bundle treemap instantly shifts performance optimization from guessing to empirical measurement.
#22. Reading the Treemap: Spotting the 4 Major Bloat Patterns
When inspecting a bundle treemap for the first time, look for these four common anti-patterns:
#3Anti-Pattern 1: Legacy Heavyweight Libraries
Certain popular legacy libraries were written before tree-shaking existed and include massive locale datasets or internal utility structures.
Common Offenders:
moment.js(~280KB minified) → Includes all international timezone and locale files by default.lodash(Full import) (~70KB) → Importing from'lodash'instead of'lodash-es'or specific sub-paths pulls in the entire utility suite.rxjs(Full bundle) (~60KB) → CommonJS imports break dead-code elimination.
#3Anti-Pattern 2: Duplicate Dependency Versions
When two different npm packages in your node_modules specify conflicting version requirements for a shared dependency (e.g., Package A requires tslib@^1.0.0 while Package B requires tslib@^2.0.0), npm/yarn/pnpm installs both versions.
Your final production bundle will contain two complete copies of the same library, wasting bandwidth and execution memory.
#3Anti-Pattern 3: Server-Side Leaks into Client Bundles
A major risk in full-stack frameworks (Next.js, Remix, SvelteKit) is accidentally importing a server-side module into a client component:
// DANGER: Importing server SDK inside a client component
import { S3Client } from '@aws-sdk/client-s3'; // Adds 150KB to client bundle!
import nodemailer from 'nodemailer'; // Pulls Node.js built-ins!If a client-side component imports a utility file that imports a server SDK, the bundler will attempt to polyfill Node.js built-ins (crypto, stream, buffer) and include the entire SDK in the client JavaScript file.
#3Anti-Pattern 4: Monolithic Single-Chunk Architecture
If your analyzer shows only one massive main.js chunk containing code for your landing page, user dashboard, admin portal, and settings settings pages, your application lacks Code-Splitting. Users visiting your landing page are forced to download the entire admin dashboard code before the page can become interactive.
#23. Step-by-Step Bundle Reduction Strategy
Follow this tiered remediation strategy to shrink your bundle size:
#3Level 1: Fix Destructuring Imports (Instant 50KB+ Savings)
Improper import syntax is the #1 cause of accidental bundle bloat.
// WRONG: Pulls in the ENTIRE lodash library (70KB)
import { cloneDeep, merge } from 'lodash';
// RIGHT: Import specifically from lodash-es or specific path (5KB)
import cloneDeep from 'lodash/cloneDeep';
import merge from 'lodash/merge';// WRONG: Pulls in all 5,000 icons from material-ui (2MB+)
import { CheckIcon, UserIcon } from '@mui/icons-material';
// RIGHT: Direct path imports allow tree-shaking
import CheckIcon from '@mui/icons-material/Check';
import UserIcon from '@mui/icons-material/User';#3Level 2: Swap Heavyweight Libraries for Modern Alternatives
Replace legacy dependencies with modern, lightweight equivalents designed for zero-dependency tree-shaking:
| Heavy Library | Size | Lightweight Replacement | New Size | Savings |
|---|---|---|---|---|
moment.js | ~280 KB | date-fns or dayjs | ~2 KB – 8 KB | 97% smaller |
lodash | ~70 KB | Native ES2024 JS or radash | 0 KB – 4 KB | 94% smaller |
axios | ~32 KB | Native fetch() API | 0 KB | 100% smaller |
uuid | ~12 KB | Native crypto.randomUUID() | 0 KB | 100% smaller |
numeral | ~25 KB | Native Intl.NumberFormat | 0 KB | 100% smaller |
#3Level 3: Enforce Tree-Shaking in package.json
Tree-shaking relies on ES Module syntax (import/export) to statically analyze which functions are actually called. CommonJS (require/module.exports) prevents static analysis.
To ensure your bundler aggressively removes unused code:
- Verify all internal packages use ES Modules.
- Add
"sideEffects": falseinpackage.json(or specify an array of files that contain side effects like CSS imports):
// package.json
{
"name": "my-app",
"sideEffects": [
"*.css",
"*.scss"
]
}This tells Webpack/Vite that any unreferenced exports in pure JS/TS files can be safely eliminated during minification.
#3Level 4: Eliminate Unnecessary Polyfills
Legacy build configurations often include polyfills for modern browser features (Promise, fetch, Object.assign, Symbol, Array.prototype.includes).
In 2025, over 98% of active web browsers natively support ES2022+ standards. Including core-js polyfills adds 30KB–80KB of dead code to modern browser downloads.
// WRONG: Global core-js import adds 80KB to all client downloads
import 'core-js/stable';
import 'regenerator-runtime/runtime';
// RIGHT: Target modern browsers in browserslist (e.g., > 0.5%, last 2 versions, not dead)Use poly-fill.io alternatives or target modern JS syntax output (target: 'es2022') in your bundler to eliminate polyfill overhead for modern browsers.
#25. Implementing Route-Based and Component Code-Splitting
Code-splitting breaks your monolithic application into smaller "chunks" that are fetched dynamically on demand.
#3Route-Based Splitting in React (React.lazy)
import React, { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Heavy admin dashboard loaded ONLY when navigating to /admin
const AdminDashboard = lazy(() => import('./pages/AdminDashboard'));
const SettingsPage = lazy(() => import('./pages/SettingsPage'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div className="spinner" />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/admin/*" element={<AdminDashboard />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}When code-splitting is configured, the bundle analyzer treemap will show HomePage.js, AdminDashboard.js, and SettingsPage.js as separate, smaller blocks. A homepage visitor downloads only 80KB instead of 850KB.
#25. CI/CD Bundle Budget Guardrails
To prevent team members from accidentally introducing a 500KB dependency in a future pull request, automate bundle size checks in CI/CD.
#3Using bundlesize or size-limit in GitHub Actions
Add @size-limit/preset-app to your project:
// .size-limit.json
[
{
"path": "dist/assets/*.js",
"limit": "150 kB"
}
]# GitHub Actions: Fail PR if bundle exceeds size limit
name: Bundle Budget Check
on: [pull_request]
jobs:
check-budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npm run build
- run: npx size-limitIf a pull request introduces an un-tree-shaken library that pushes the bundle size past 150KB, the GitHub Action automatically fails and blocks merging.
#26. How to Audit Bundles Locally & Privately
Generating bundle statistics (stats.json) is standard in modern build setups. However, uploading stats.json or compiled production JS code to cloud-based "online bundle visualizers" can expose internal application logic, proprietary module names, and source map structures to remote servers.
Use the AllDevToolsHub Bundle Analyzer:
- 100% Client-Side Processing: Upload your
stats.jsonor JS bundle files directly into your browser. - WASM Treemap Engine: Renders interactive treemaps locally using your browser's CPU.
- Zero Data Leakage: Verify with browser DevTools (Network tab) that zero bytes leave your computer.
#3Source Maps and Production Security
While source maps (.map files) are valuable for debugging and bundle analysis, publishing production .map files publicly to your Web host presents security risks:
- Source Code Exposure: Anyone can inspect your complete un-minified TypeScript/JavaScript source code using browser DevTools.
- Internal Comment Leakage: Developer comments, internal API endpoints, and architectural notes become visible.
- Hidden Endpoint Discovery: Security researchers and attackers can discover hidden
/adminroutes and un-launched feature flags.
Recommended Production Source Map Strategy:
- Generate source maps during build (
hidden-source-mapmode). - Upload source maps to your private error tracking service (Sentry, Datadog, Bugsnag).
- Delete
.mapfiles from the public/distdirectory before CDN deployment. - Analyze bundle composition locally using
stats.jsonwithout deploying public source maps.
#27. Configuring Bundle Analyzers in Vite & Webpack
Setting up an automated bundle analyzer in your build configuration takes less than two minutes:
#3Vite Setup (vite-plugin-visualizer)
// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({
filename: './dist/stats.html',
open: false,
gzipSize: true,
brotliSize: true,
}),
],
});#3Webpack Setup (webpack-bundle-analyzer)
// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'disabled', // Generate stats.json file only
generateStatsFile: true,
statsFilename: 'stats.json',
}),
],
};Generating stats.json or stats.html as a build artifact allows developers to inspect bundle composition locally or upload the json payload to an offline, browser-based visualizer.
#2Summary
Controlling JavaScript bloat is essential for maintaining fast Web Vitals (INP and LCP):
- Visualize Your Treemap: Use a bundle analyzer to inspect real minified module sizes.
- Audit Imports: Replace full library destructuring with specific sub-path imports.
- Swap Legacy Packages: Replace
moment,lodash, andaxioswith lightweight or native alternatives. - Implement Code-Splitting: Use
React.lazy()or framework routing to isolate heavyweight components. - Set CI/CD Budgets: Enforce hard bundle limits to prevent regression.
Analyze your JavaScript bundle privately at the AllDevToolsHub Bundle Analyzer.
#2Related Tools
- Bundle Analyzer, Visualize and inspect production JS bundle stats locally
- CSS Minifier, Compress production CSS stylesheets
- Image Compressor, Optimize media assets alongside JavaScript code
#2Related Articles
- Web Performance in 2025: Beyond Core Web Vitals
- Web Performance Audit: A 10-Minute Workflow
- The ROI of a 10ms Workflow
#2Frequently Asked Questions
Q: What is the difference between Stat Size, Parsed Size, and Gzipped Size?
A: Stat Size is the un-minified size of source files before compilation. Parsed Size is the actual size of the minified JavaScript code generated by the build tool; this is what the browser CPU must parse and compile. Gzipped / Brotli Size is the compressed file size transferred over the network. Gzipped size determines download speed, while Parsed size determines CPU execution latency (INP impact).
Target Production JavaScript Budgets:
- Initial Load (Un-gzipped): < 200 KB
- Initial Load (Gzipped): < 60 KB
- Route-specific Chunks: < 50 KB per route
Keeping initial JavaScript payloads under 60 KB gzipped guarantees sub-200ms INP execution on 95% of mobile devices globally.
Q: How do I generate a stats.json file in Next.js?
A: Install @next/bundle-analyzer, wrap your next.config.js with withBundleAnalyzer({ enabled: process.env.ANALYZE === 'true' }), and run ANALYZE=true npm run build. Next.js will generate client and server bundle statistics during the build phase.
Written by Rahul Jalavadiya, founder of AllDevToolsHub. All tools run locally in your browser.
#2Sources / Further reading
- webpack - Bundle Analyzer plugin
- MDN Web Docs - JavaScript performance
- web.dev - Reduce JavaScript payloads
- Source Map Explorer - Analyze bundle sizes
Quick Summary
A Bundle Analyzer creates a visual "treemap" of your JavaScript application. It shows you exactly which libraries (like Moment.js, Lodash, or large UI kits) are taking up the most space in your production build. Reducing bundle size is the fastest way to improve your site's "Interaction to Next Paint" (INP) score.
Key Takeaways
- Treemaps help you see the "physical" size of your code dependencies.
- Large utility libraries (like Moment.js) can often be replaced by smaller natives (like `date-fns` or Intl).
- Tree-shaking is your best friend; ensure your libraries are exported as ES Modules.
- Every 100KB of JavaScript can take 1 second to parse on a low-end mobile device.
When to use it
- Auditing a React/Next.js or Vue application for performance bottlenecks.
- Deciding whether to add a new library to your project.
- Justifying a refactor to stakeholders by showing "visual bloat."
- Monitoring bundle size growth over time in your CI/CD pipeline.
Common Mistakes
- Importing an entire library when you only need one function (e.g., `import { map } from 'lodash'` vs `import map from 'lodash/map'`).
- Including "Dev Dependencies" in your production bundle.
- Not using "Code Splitting" for routes that users rarely visit.
- Ignoring the impact of "Duplicate Dependencies" (having multiple versions of the same library).
Bundle Analyzer Guide: Identifying and Fixing JavaScript Bloat, Frequently Asked
What is the best Bundle Analyzer tool?
For Webpack, `webpack-bundle-analyzer` is the standard. For Next.js, use `@next/bundle-analyzer`. For Vite, use `rollup-plugin-visualizer`. AllDevToolsHub also offers an [online Bundle Analyzer](https://alldevtoolshub.com/bundle-analyzer) for quick checks.
What is "Tree Shaking"?
It's a build-step process that removes "dead code" (functions or components you imported but never actually used) from your final bundle.
How big should my JavaScript bundle be?
Aim for less than 170KB (compressed) for your "Critical" bundle. Anything larger will start to significantly impact performance on mobile networks.
Tools Mentioned in This Article
Regex Tester
Test and debug regular expressions with live matches.
JS Obfuscator
Obfuscate JavaScript code with variable renaming, string encoding, and dead code injection.
JS/TS Beautify/Minify
Lightweight JavaScript/TypeScript formatter and minifier.
Network Speed Test
Measure your download speed and latency in the browser.
Tools, tactics, and toughened-up tips, once a week
New tools, deep-dives on developer workflows, and the occasional gem we found this week. No spam, no tracking. Unsubscribe anytime.
Found an error or have feedback?
We correct errors quickly and document changes in our changelog. Report issues at support@alldevtoolshub.com.