A performance budget is a limit you refuse to cross. Performance monitoring is a dashboard you look at after the damage is done. Teams frequently confuse the two, installing a monitoring tool, watching scores drift downward for months, and calling it “performance management.” It isn’t. A budget only works if crossing it breaks the build.
That distinction matters because it changes where enforcement happens. Monitoring lives in production, reacting to what already shipped. A budget lives in CI, blocking a pull request before a bloated bundle or an unoptimized hero image ever reaches a user. If your current process can’t stop a merge, you don’t have a budget — you have a graph.
Below is a ranked breakdown of the five performance budget types most worth setting up, ordered by how much return they typically deliver relative to the effort of implementing them.
1. JavaScript Bundle Size Budget
Nothing degrades web performance faster than shipping too much JavaScript, and nothing is easier to measure. This is why it tops the list: high impact, low setup cost.
A JavaScript budget sets a hard ceiling on the size of your compressed, shipped bundles — typically measured per route or per entry point, not as one aggregate number for the whole site. A single site-wide limit hides the fact that your checkout page might be lean while your blog homepage is loading a 400KB chart library nobody uses above the fold.
How to set it:
- Start by measuring your current gzipped (or Brotli) bundle sizes per route using your bundler’s stats output.
- Set the initial budget at your current size, not an aspirational one. A budget you’re already failing gets ignored within a week.
- Reduce the number gradually — a 10% cut per quarter is realistic; a 50% cut announced on Monday is not.
- Enforce it with a tool like
bundlesize,size-limit, or a custom webpack/Rollup plugin that fails the build on overage.
Typical starting targets:
| Route Type | Reasonable JS Budget (gzipped) |
|---|---|
| Marketing / landing page | 100–150 KB |
| Content page (blog, docs) | 150–200 KB |
| Interactive app shell | 250–350 KB |
If your team ships one thing from this list, make it this one. Bundle size is the root cause behind a large share of Core Web Vitals failures, particularly for Interaction to Next Paint.
2. Largest Contentful Paint (LCP) Budget
LCP is the metric most directly tied to how fast a page feels, which makes it the second most valuable budget — though it’s harder to enforce than a simple file-size limit because it depends on runtime conditions, not just build output.
A useful LCP budget isn’t a single number pulled from Google’s “good” threshold (2.5 seconds). It should be set relative to your own historical p75 value, then tightened over time. Chasing an arbitrary 2.5-second target on a lab test means little if your real-world 75th percentile sits at 4.2 seconds.
How to set it:
- Pull your current p75 LCP from CrUX data or your RUM provider.
- Set the budget slightly below that value — enough to force improvement, not so far below that it’s unreachable.
- Tie the budget to CI using Lighthouse CI’s
assertconfiguration, which can fail a build if LCP in a synthetic test exceeds your threshold. - Re-check the budget every quarter against fresh field data. Lab numbers drift from field numbers; don’t let the gap grow unnoticed.
The catch with LCP budgets is that they’re a symptom metric — they tell you something is slow, not what. Pair this budget with the JavaScript and image budgets below, and you’ll usually find the actual cause faster than staring at the LCP number alone.
3. Image Payload Budget
Images are usually the single heaviest asset type on a page by raw bytes, yet they get less enforcement attention than JavaScript because the failure mode is quieter — a slow-loading hero image doesn’t throw a console error the way a bloated bundle might.
A payload budget for images caps the total transferred size of image assets on a given page, not just the dimensions or format. A perfectly sized 200px avatar exported as an uncompressed PNG can still weigh more than a properly compressed 2000px JPEG.
How to set it:
- Set a per-page ceiling for total image weight — 500KB is a reasonable starting line for content-heavy pages, tighter for landing pages.
- Enforce modern formats (WebP or AVIF) as a build-time requirement, not a recommendation designers can skip.
- Add automated checks using tools like
imageminin a pre-commit hook, or a CI step that flags any image over a set file size. - Budget the LCP image separately and more strictly than the rest, since it directly affects your LCP score.
This budget is the easiest to violate accidentally — a CMS upload, a marketing asset dropped in without optimization — so it benefits from automated gatekeeping more than any item on this list.
4. Third-Party Script Budget
Third-party scripts are the budget category teams most often forget to own, because the code isn’t theirs. Analytics tags, chat widgets, ad scripts, and A/B testing tools accumulate slowly, each addition justified individually, until the combined weight rivals or exceeds the site’s own JavaScript.
How to set it:
- Cap the number of third-party domains loaded per page. Five is a reasonable ceiling for most sites; each additional domain adds a DNS lookup and connection cost.
- Set a byte budget specifically for third-party JavaScript, tracked separately from first-party code so one doesn’t hide overages in the other.
- Require every new third-party script to load with
asyncordefer— never a blocking, unattributed<script>tag — as a condition of approval, not an afterthought. - Review the list quarterly. Tags for discontinued campaigns or abandoned tools rarely get removed unless someone is assigned to check.
This budget ranks fourth rather than higher because it requires organizational enforcement — marketing and product teams need to agree to the rule — rather than a purely technical fix. The technical part is simple; the coordination part is not.
5. Time to Interactive (TTI) / Total Blocking Time (TBT) Budget
Rounding out the list is a budget on main-thread responsiveness, most practically measured through Total Blocking Time in lab tools like Lighthouse. It ranks last not because it’s unimportant, but because it’s largely a downstream effect of budgets 1 and 4 — fix your JavaScript and third-party weight, and TBT usually improves on its own.
How to set it:
- Set an initial TBT ceiling of 200ms for lab tests, tightening toward 150ms as other budgets improve.
- Run this check in Lighthouse CI on every pull request that touches client-side code, not just before major releases.
- Treat repeated failures as a signal to profile the main thread rather than immediately adding more compute budget — the fix is usually removing work, not tolerating more of it.
Because TBT is sensitive to test environment noise, run it on a throttled CI machine with consistent CPU slowdown settings; comparing an unthrottled local run against a throttled CI run will produce numbers that don’t line up and erode trust in the budget itself.
Putting the Ranking to Work
Setting up all five budgets in one sprint isn’t realistic, and trying to will likely mean none of them stick. Start with the JavaScript bundle budget — it’s the cheapest to implement and the enforcement mechanism (CI failing on file size) is nearly copy-paste. Once that’s holding, add the image payload budget, since the tooling overlaps closely with what you already built for bundles.
LCP and third-party budgets take longer because they require field data and cross-team buy-in respectively. Save TBT for last; it will likely improve as a side effect of the earlier work, making it easier to set a realistic target once you get there.
A budget that nobody enforces is just a number on a slide. Pick one from this list, wire it into your CI pipeline this week, and let it fail a build before it fails your users.
🔗 Recommended Reading
- The Repeat-Visit Paradox: How Service Workers Quietly Reshape Core Web Vitals
- Web Workers vs. Debouncing: The Real Fix for Slow INP Scores
- Edge Computing and Core Web Vitals: What Actually Moves the Needle
- Your DOM Has a Weight Problem: A Step-by-Step Guide to Fixing Excessive DOM Size
- Core Web Vitals in Single-Page Applications: Why the Basics Aren't Enough