A useful CI bundle performance budget should measure the costs users actually experience, not just a single JavaScript kilobyte cap. In practice, budget five things together: compressed transfer size, uncompressed shipped code, entrypoint and async chunk shape, request and third-party counts on critical routes, and a small set of lab timings. That combination catches regressions that a single bundle-size number misses and gives engineers a clearer path to fixing them.
What should the budget surface include?
Start with the build artifacts your bundler can report every run. For each critical route you choose to test, set limits for initial script transfer size and total page transfer size. Lighthouse budget documentation shows that a single budget file can combine timing, resource-size, and resource-count thresholds, including total script bytes, total page bytes, third-party request counts, and metrics such as interactive time. Use those checks on the critical routes you test in CI.
Then add static bundle structure checks from your bundler output:
- initial entrypoint JS size after compression
- uncompressed JS size for the same entrypoint, as a proxy for parse and compile cost
- max async chunk size for deferred routes
- number of initial chunks or requests needed before the app is usable
- duplicated package weight across chunks
This matters because bundlers can produce regressions that are not obvious from one total number. The webpack code splitting guide shows that multiple entry chunks can duplicate shared dependencies and demonstrates de-duplication with shared chunks or SplitChunksPlugin.
Why is a simple KB cap not enough?
A single cap misses at least four common failures.
First, duplicated dependencies across chunks can keep the main page under budget while making total downloaded code worse. Second, too many tiny chunks can reduce bytes per file while increasing request overhead and startup coordination. Third, compressed size can look healthy while uncompressed code volume still drives parse and compile work on the main thread. Fourth, third-party scripts can consume the initial path even if your own bundle stayed flat.
That is why CI should gate both artifact data and lab measurements. Lighthouse budget documentation is useful here, and its guidance is also clear that Lighthouse is based on lab data, so use it as a repeatable gate and compare it with field data outside CI when available.
How should budgets differ by route?
Do not use one global threshold for the whole app. Budget the landing page, authenticated app shell, and heavy feature routes separately.
Example checklist:
- Marketing landing page: tight initial script and total-page transfer limits; low third-party count.
- Authenticated app shell: moderate initial budget, strict cap on duplicated vendor code.
- Deferred report builder: larger async chunk allowance, but no growth in the shell budget.
For Vite projects, run vite build in CI and review chunking strategy explicitly. The Vite build guide documents that vite build produces the production bundle. It also notes that chunk splitting can be configured through Rolldown output options; if you use a framework, check that framework’s chunking guidance too. That boundary matters: generate production artifacts first, then run budget checks against those exact artifacts. Do not mix dev-mode output with CI thresholds.
What should happen when CI fails?
Make the failure message diagnostic, not just punitive. Report which route failed, whether the regression was transfer size, uncompressed size, request count, or timing, and list the top changed chunks.
Typical remediations are straightforward: enable or refine split-chunk deduplication, move route-only code behind dynamic imports, replace a heavy dependency, verify tree shaking, or push non-critical work off the initial path. In webpack, shared-chunk settings and SplitChunks guidance are the first place to look for duplicate vendor weight.
Questions about compression metrics and thresholds
Should CI budget gzip or brotli?
Pick one primary transfer metric and keep it consistent. If production serves brotli, track that for transfer budgets, but keep uncompressed JS as a separate guardrail.
Should every budget be absolute?
No. Keep a few hard caps for the initial route, then add regression thresholds against the current baseline so deliberate exceptions can be reviewed without normal growth slipping through.
Next step: define three route-specific budgets this week, wire Lighthouse plus bundler stats into one CI report, and fail builds only when the report identifies the exact regressing chunk or request class.
Reviewed: 2026-09-06.
Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.
Read our editorial approach ↗