Two slots open this quarter Pune, India / 13:00–22:00 IST React and Node only Book a 30 minute call
reactdevstudio
Contact Book a call
Work Hire React developers Hire Node developers Node services Offshore development Blog About Contact
Book a 30 minute call
Home/Blog/Setting a performance budget a client will accept
performance / react 9 min read Updated August 2026

Setting a performance budget a client will accept

LCP targets, bundle limits, and how to write them into a contract in a way that is enforceable by both sides rather than aspirational on one.

Post author

Yogesh Jadhav

Founder, reactdevstudio · Pune

A performance budget that lives in a wiki is a wish. A performance budget that fails the build is a constraint, and constraints are what actually produce fast software. The difference between the two is about a day of setup and one uncomfortable conversation held early rather than late.

The short version

Pick three numbers, not fifteen. A budget nobody remembers is a budget nobody applies.

Measure on a mid-range Android over a throttled connection, not a laptop on office wifi.

Fail the build on a regression. A warning is a suggestion and will be ignored under deadline.

Write the budget into the statement of work with a named measurement method, or it is not a term.

Agree what happens when the client's own tag manager breaks the budget. That is the usual cause.

Which numbers to pick

Three, chosen so that each one catches a different failure. More than three and nobody carries them in their head; fewer and something important goes unwatched.

MetricTargetWhat it catches

LCP

Under 2.5s at p75

The user-visible one, and a Core Web Vital. Usually an image, a font, or a render blocked on data.

INP

Under 200ms at p75

Interaction responsiveness. Catches the expensive re-render nobody profiled.

JS transferred

Under 200KB compressed

The leading indicator. It rises quietly, one dependency at a time, and drags the other two with it.

The third is the one worth arguing for, because it is the only one that is a leading indicator rather than a lagging one. LCP and INP tell you that you already have a problem. Bundle size tells you a month beforehand, on the pull request that introduced it, while it is still one line to revert.

CLS is deliberately absent. It matters, but it is usually fixed once, by reserving space for images and ads, and then stays fixed, which makes it a poor thing to spend one of three slots on.

These three are also the numbers we quote in the case studies, where the before and after figures are stated rather than described.

How to measure it honestly

Most performance disputes are measurement disputes. The developer measured on a laptop on office wifi and the client measured on a phone on mobile data, and both are reporting accurately.

  1. 01

    Fix the device and the network

    A mid-range Android profile with 4x CPU throttling and a slow 4G network profile. Write those exact words into the agreement.

  2. 02

    Fix the pages

    Three URLs: the busiest entry page, the heaviest logged-in page, and one representative detail page. Budgets are per page, not per site.

  3. 03

    Measure in CI, on every pull request

    Automated, on a fixed runner, with the numbers posted into the PR. Manual measurement happens when someone remembers, which is never under deadline.

  4. 04

    Take the median of five runs

    A single run is noise. Five runs and the median removes most of the argument about whether a regression is real.

  5. 05

    Track field data separately

    Lab numbers catch regressions; field data tells you the truth about real users. Use lab numbers for the gate and field data for the target.

"Half the arguments about performance are two people measuring different things on different hardware and both being right."

Measurement discipline is the same thing we look for when hiring, which is why it is one of the four React interview questions we actually send.

Making the budget actually bite

A budget only works if crossing it stops something. Everything else is a dashboard nobody opens after week three.

Fail the build A pull request that pushes the bundle past the limit does not merge. Not a warning: a failure, with an override that requires a named person.
Show the delta Post the change, not the absolute number. "+34KB" prompts a conversation; "212KB" prompts nothing.
One override path Someone senior can approve crossing it, and the reason is recorded. Budgets with no escape valve get deleted the first time they are inconvenient.
Review quarterly Budgets that never move are budgets nobody believes. Adjust them deliberately rather than eroding them silently.

Where our interests differ from yours

A hard performance budget slows us down and occasionally means telling you that a library you asked for costs more than it is worth. We propose them anyway because the alternative is a rebuild conversation in eighteen months. Be aware that a vendor who cheerfully accepts any budget you name may simply be planning not to measure it.

A budget written into a statement of work is a commercial term, and the rest of those terms are set out in the contract checklist.

Writing it into the contract

This is where most budgets fail, because "the site should be fast" is not a term anybody can enforce or defend. A usable clause has four parts, and it protects the vendor as much as the client.

The four parts of an enforceable clause

01 The metric and the number: LCP under 2.5 seconds at the 75th percentile.
02 The measurement method: named tool, named device profile, named network profile, median of five runs.
03 The pages it applies to, listed as URLs or route patterns.
04 What is excluded: third-party scripts added by the client after handover, and any tag manager content.

The fourth line is not vendor self-protection dressed up as fairness. In practice the most common cause of a blown budget is a marketing tag added through a tag manager six weeks after launch, with nobody measuring. Agreeing in advance that this is outside the vendor's control, and more usefully that the tag manager is itself inside the monitoring, is what stops that from becoming a dispute.

Agreeing it during scoping rather than after launch is also cheaper, for the reasons in what React development costs.

Where the wins usually are

When a React application misses a budget, it is nearly always one of these, in roughly this order.

An unoptimised hero image

The single most common cause of a bad LCP. Correct format, correct dimensions, priority hint. An afternoon, usually.

Fonts blocking the render

Self-host, preload the two weights you use, and set a sensible fallback. Cheap, and immediately visible.

One enormous dependency

A date library, a chart library, an icon set imported whole. Check the bundle report before blaming the framework.

Everything on one route

No code splitting, so the settings page ships with the landing page. The highest-use structural fix.

Waterfall data loading

Three sequential requests before anything renders. Usually parallelisable, sometimes by moving one call to the server.

Third-party scripts

Analytics, chat widgets, tag managers. Frequently larger than the entire application, and rarely audited.

Measure first

A bundle analyser and one profile run before touching anything. The cause is usually not where the argument assumes.

Fix the top item only

Then measure again. Performance work done in batches makes it impossible to tell what helped.

Then add the gate

Fix it once, then make it impossible to break again. Doing it the other way round is a week of red builds.

Server rendering is sometimes offered as the fix for a bad first paint. It sometimes is, and React or Next.js is the more careful version of that argument.

The conversation when you miss it

A budget will be missed. If the first time that happens the response is defensive on one side and accusatory on the other, the budget does not survive the quarter. Agreeing the shape of that conversation in advance is what turns a number into a working agreement.

Establish the cause first A regression from application code, a third-party script, a new image, or a change in the measurement itself. These have completely different owners and remedies.
Say the size of it "LCP moved from 2.1s to 2.9s on the pricing page" is actionable. "Performance has got worse" starts an argument about whether it has.
Offer the trade explicitly Reverting the feature, deferring the script, or accepting the number and revising the budget. All three are legitimate; picking silently is not.
Record the decision Whichever is chosen, write down who chose it and why. This is what stops the same regression being re-litigated in six months.

The third row is where good vendors distinguish themselves and where we have got it wrong before. The instinct when a budget is missed is either to hide it until it can be fixed, or to present it as the client's fault for asking for the feature. Both are worse than showing the measured number and the three options, because a performance budget is a trade-off framework, not a pass mark.

It is also worth agreeing that revising the budget upward is allowed. A target nobody can hit gets switched off, and a switched-off gate is worse than a slightly looser one that still runs. Adjusting it deliberately, with the reason recorded, keeps the mechanism alive.

If the regression came from an engineer who has just joined, that is an onboarding problem rather than a skill one — see the first two weeks in how to hire React developers.

Questions people ask

+What if the client insists on a chat widget that breaks the budget?

Show the measured cost, in the numbers you both agreed, and let them decide. It is their product and the trade may be worth it. What matters is that it is a decision with a price attached rather than a silent regression discovered a quarter later.

+Are Core Web Vitals still a ranking factor?

They are part of page experience signals, and they are not going to outrank relevance. The stronger argument for the budget is conversion and bounce rate rather than ranking, and it is the one that holds regardless of what search engines do next.

+Is 200KB of JavaScript realistic for a real application?

For an initial route, yes, with code splitting, and it takes discipline from the first week rather than a cleanup at the end. If you are starting from 900KB, set the budget at your current number minus ten per cent and ratchet it down each quarter. An unreachable target is ignored immediately.

+Should the budget apply to the logged-in app as well as the marketing site?

Yes, with different numbers. Logged-in users tolerate a slower first load and are far less tolerant of slow interactions, so weight INP more heavily there and LCP more heavily on public pages.

Where to go next

If the budget is being set as part of a scope, what React development costs covers how targets like this affect the estimate. If server rendering is on the table as a performance fix, React or Next.js is the more careful version of that argument. Budgets like these go into our statements of work, which the hire React developers page describes.

Sources

Post author

Yogesh Jadhav

Founder of reactdevstudio. Ten years as a working full stack developer, across SaaS products and client work.

Related, Group B only

Three more on engineering

Engineering posts only, scored within Group B, so a technical reader is never handed a procurement checklist.

react / hiring 14 min

How to hire React developers, the process written out

nodejs / typescript 9 min

Node.js versus Python for your backend

cost / react 10 min

What React development costs, and what moves the number

Two slots open this quarter.

Send the ticket you would hand over first. You get a written approach in two working days.

Book a 30 minute call

Budgets we will actually sign up to, in the SOW.