JavaScript Bundle Budget
Set enforceable JS size budgets per route, measure with CI, and cut payload without cargo-cult micro-opts.
- performance
- bundle
- budget
- javascript
- ci
JavaScript costs download + parse + compile + execute on the main thread. A bundle budget is an explicit size (and sometimes cost) limit per page or route so regressions fail CI before users feel them.
Docs: web.dev page weight, Performance budgets, bundles.
Why budgets beat vibes
Without a number, every PR “only adds 8KB.” Six months later the cart route is +400KB gzipped. Budgets make tradeoffs visible: new chart library needs an equal cut elsewhere.
What to budget
| Metric | Example target (illustrative) |
|---|---|
| JS transferred (gzip/br) | Home ≤ 150KB, App shell ≤ 200KB |
| JS uncompressed | Parse cost proxy |
| Third-party JS | ≤ 50KB on critical templates |
| Total requests | Soft limit |
Targets depend on audience devices — use field data to set, not blog defaults.
Measuring locally
# vite/webpack analyzers
npx vite-bundle-visualizer
# or source-map-explorer dist/app.js
Track compressed size delivered to users (what Network shows transferred) and raw for parse cost.
Enforce in CI
bundlesize / lighthouse CI / size-limit / custom script
// size-limit style idea
module.exports = [
{ path: 'dist/assets/index-*.js', limit: '180 KB' },
{ path: 'dist/assets/vendor-*.js', limit: '120 KB' },
];
Fail PRs on regression > threshold. Allowlisted exceptions need owner + removal date.
How to cut JS
- Route/component code-split — code splitting
- Tree-shake and avoid barrel imports of huge libs — tree shaking
- Replace heavy deps (moment → dayjs/Temporal polyfill strategy)
- Defer third parties — third-party cost
- Prefer CSS for simple UI; don’t pull a library for one tooltip
Interaction with CWV
Large JS hurts LCP (if blocking), TBT, and INP (parse + long tasks). Budgets are a leading control; CWV is the lagging user metric.
Interview out-loud
“A JS budget is a CI-enforced size limit per route. I measure compressed transfer, visualize chunks, split routes, and reject dependency bloat. Budgets prevent death by a thousand 8KB PRs.”
How this shows up in interviews
Be ready to define the metric or technique in one sentence, name one measurement approach (DevTools panel, web-vitals, or headers), and cite a concrete fix you would try first. Walk through a before/after: what the waterfall or flame chart showed, what you changed, and which percentile moved. Mention a tradeoff (complexity, caching correctness, or third-party business constraints) so the answer doesn’t sound like a blog checklist.
Production guardrails
Ship behind a flag when the change is risky, watch field p75 for the affected template for at least a few days, and keep a rollback path. Pair lab verification (throttled Performance/Network) with RUM so you don’t celebrate a Lighthouse-only win. Document the owner of any ongoing budget or third-party exception.
Related
Further depth
Teams often under-invest in this topic until an incident or CWV regression. Schedule a one-hour drill: reproduce the failure mode in DevTools, list the top three mitigations for your stack, and file tickets with owners. Revisit after the next major feature that touches networking, rendering, auth, or third parties — those are the moments regressions land. Keep primary documentation links in the runbook so on-call is not searching chat history at 2am.
Concrete artifacts to leave behind: a short architecture note, a CI assertion or header snapshot, and a dashboard panel (lab or field) that would have caught the last bug. Teaching the rest of the team the mental model matters as much as the one-line fix.
Further reading
Related guides
- Code Splitting StrategiesRoute, component, and vendor splits: dynamic import(), magic comments, and avoiding over-splitting waterfalls.
- Performance Budgets in CIEncode LCP/size budgets in CI with Lighthouse CI or size-limit so regressions fail pull requests.
- Tree Shaking BasicsDead-code elimination for ES modules: sideEffects, named imports, and why some libraries won’t shake.
- Avoiding Layout ThrashingStop forced sync layout loops: batch DOM reads and writes, use rAF, and fix janky measurement code.
- Caching Static Assets FingerprintingContent-hash filenames, long-cache headers, HTML revalidation, and CDN invalidation without stuck users.