Skip to content
Strynal, Digital Agency

Engineering 6 min read

How to Set a Web Performance Budget

Learn how to define quantity, timing, and rule-based limits for your site, set them against real benchmarks, and enforce them in CI so performance holds.

By Strynal Team

A performance budget is a set of limits your team agrees not to cross. Without one, performance decisions get made by default. That default is almost always “ship it, optimise later,” which is how sites accumulate 4MB JavaScript bundles no individual change ever gets blamed for.

What a performance budget is (and is not)

A budget is a constraint, not a measurement. A measurement tells you where you are. A budget tells you where you stop. The two work together, but confusing them produces teams that measure obsessively and still ship slow sites.

Budgets fall into three categories, and they are not interchangeable.

Quantity metrics cap raw deliverables: total JavaScript, total image weight, number of HTTP requests, time to first byte. They are easy to check in CI and easy to reason about. “No more than 200KB of parsed JS on the home page” is a budget anyone can verify.

Timing metrics cap the experience: Largest Contentful Paint, Time to Interactive, Total Blocking Time. These are the numbers real visitors feel. The downside is that they are harder to enforce reliably in CI because they vary with the test device and network conditions.

Rule-based metrics mix the two: “no single image above 100KB uncompressed” or “no render-blocking script in the <head>.” These work as guard-rails during code review even when your pipeline does not automate them.

Most teams benefit from all three layers, applied to different points in the build process.

Choose your budget metrics carefully

The instinct is to budget everything. Budget the things that actually hurt.

For most marketing and editorial sites, the highest-value budgets are:

  • Total JavaScript (parsed and executed). JS is the heaviest resource per byte because it blocks the main thread. A byte of image takes time to transfer; a byte of JS takes time to transfer and then more time to parse and run.
  • LCP (Largest Contentful Paint). It is the metric closest to “did the page load?” in a visitor’s head, and it is what Google’s ranking signal grades on. If you want one number to govern a page, this is it. Read the full breakdown in our Core Web Vitals guide.
  • Third-party request count. Tag managers, chat widgets, affiliate scripts. These are the category where budgets collapse, because they are added by stakeholders who do not own the performance targets.

Metrics worth adding after those are addressed: total page weight, number of web fonts, image file count.

How to set the numbers

There is no universal answer. Here is a workable process.

Start with your competition. Run three or four competing URLs through WebPageTest on a mid-range mobile device at 4G. Those numbers are not your target; they are your floor. A budget set at the field performance of a strong competitor is a defensible starting point.

Then apply a 20% improvement target: your budget should come in 20% faster or lighter than the benchmark you want to beat. A 3-second LCP on a competitor’s site sets a 2.4-second target for yours.

A budget that matches your competitor is not a budget. It is a guarantee of a tie. Set it 20% better than the benchmark you want to beat.

For JavaScript, a useful calibration is 200KB of parsed JS for the initial route on a median Android device. That is not a universal law; adjust it against what your product actually requires. The point is to have a number before you start building, not after the bundle is already large.

Document the values in a file that lives with the code. A budget.json or a Lighthouse CI config committed to the repo is enforced. A document on a shared drive is forgotten.

Enforcing the budget in CI

Setting a budget without enforcement is a wish. The enforcement mechanism is a build step that fails the pipeline when a limit is crossed.

Lighthouse CI integrates into most CI systems and supports budgets defined as assertions over any Lighthouse audit. bundlesize is a lighter option if you only need file-weight guards. Either is worth setting up in the first sprint; retrofitting them onto a slow site is much harder.

Two things kill enforcement over time.

First, making the threshold so tight it fails constantly. A budget you override every other merge stops functioning as a signal. Set it at a number you can consistently clear, then tighten it quarterly.

Second, ignoring third-party scripts in the check. If CI only covers your own bundle and lets analytics tags load unchecked, you will pass in tests and miss budget in production. Lazy-loading third-party embeds is one of the more reliable ways to keep external weight off the critical path without removing it entirely.

Monitoring in production

CI enforcement is your gate. Production monitoring is your ground truth.

The web-vitals library reports Core Web Vitals from real sessions to any analytics endpoint. Pipe those values into your dashboards, segment by page template and device class, and track the 75th percentile rather than the mean. The 75th percentile is what Google’s CrUX grades, and it represents the visitors most likely to leave.

Slow pages cost real revenue. A monitoring loop closes the gap between “we set a budget” and “we know whether the budget is holding.” Review numbers after every major release and before any high-traffic campaign.

Trade-offs to know going in

Budgets slow down some kinds of work. A new feature may require a 150KB library you now cannot add without renegotiating the limit. That friction is the point. It surfaces the cost of the decision when there is still time to find a lighter option, reach for a native API, or decide the trade-off is worth making explicitly.

That said, budgets should be owned by the team, not handed down from a spec. A budget nobody agreed to is a budget nobody defends. The conversation about limits is also a conversation about what the product actually needs. Have it early.

How Strynal approaches performance budgets

At Strynal, performance budgets are set during scoping, not after launch. We define JavaScript weight limits, LCP targets, and third-party request caps before writing the first component, and we commit them to the repo so they run in CI from day one.

The projects we deliver through our websites and apps practice start from a baseline where image pipeline, font loading strategy, rendering approach, and caching are already accounted for. That leaves budget headroom for the features a site actually needs, rather than spending it on infrastructure weight that should never have been there in the first place.

If you are planning a build and performance is not yet part of the brief, that is the clearest sign the budget conversation is overdue.