On this page 10 sections
- A budget is a decision, not a dashboard
- Pick the units that match the problem
- A starter budget for a small marketing site
- Third parties, fonts and images
- How it relates to LCP, INP and CLS
- Measuring and failing a build without theatre
- What to do when something will not fit
- Reviewing the budget after launch
- Key takeaways
- Frequently asked questions
I keep budgets for the same reason I keep the scope tight in the iOS app MVP checklist: a number you will actually defend is more useful than a colourful report you ignore. The Performance Budget Calculator and the Performance Budget Simulator are there to help you try the numbers, not to issue a standard.
A budget is a decision, not a dashboard
Teams often start with a lighthouse chart and never write the sentence that would make it a budget: “this page may not ship more than this much JavaScript,” or “this template may not add a fourth third-party tag.” A dashboard will describe yesterday. A budget tells you whether today’s change is allowed.
The decision should be specific enough to fail a build, or to fail a pull request review, without a meeting:
- Which templates it applies to (home, articles, a checkout, a tool page).
- Which units you will measure (compressed kilobytes, request counts, a lab metric, a named third party).
- Where you measure them (the production build, not a debug bundle; a representative URL, not an empty app shell).
- What happens when something will not fit.
If no one is willing to reject a change, you do not have a budget. You have a hope. That is also how costs behave in what an iOS app really costs: an unspoken ceiling is not a ceiling.
Pick the units that match the problem
Choose a small set of units that describe the failures you actually have, and that you can measure reliably.
Good units for a small site:
- Transfer size of your own JavaScript, compressed, for a given template. This is the unit that most often gets away from a brochure site that “only added a small widget”.
- Transfer size of CSS, if the design system has started shipping every variant on every page.
- Transfer size or decoded size of the LCP image, and perhaps a cap on image bytes below the fold for a typical article.
- Font files: how many faces, and their compressed size. Two families with five weights each is a budget decision, whether or not anyone wrote it down.
- Number of third-party origins, or the compressed size and main-thread time of a named tag. The Third-Party Cost Calculator is useful before you agree to “just one more snippet”.
- Request count on a cold load of a representative page, if your waterfall is a long tail of small files.
Weaker units, if they are the only ones you have:
- A Lighthouse performance score. It moves for reasons you do not control, and it is a poor build gate unless you run it carefully and give it a wide margin.
- A field Core Web Vital. Those are the outcomes you care about, but they lag, and they are not a useful CI signal for a site with little traffic.
You can still watch field metrics. You just should not pretend they are a budget you can enforce this afternoon. The gzip Size Estimator is a better friend to a JavaScript ceiling than a once-a-month CrUX snapshot.
A starter budget for a small marketing site
There is no official budget. The following is a starting point I would try for a small marketing or content site — a home page, a few service pages, and articles — not for a rich web app, and not as a universal rule. Measure your current production templates first, then tighten towards something like this rather than inventing a fantasy site you do not have.
For a typical article or marketing template, on a mobile-sized cold load:
- Your own JavaScript: well under 150 KB transferred, and much less if the page is mostly text. A site like this one can live far below that if the tools on a page do not pull in a large framework for the chrome.
- Your own CSS: a single, reasonable bundle, not a copy of the entire design system plus three unused plugins. If you are already over 100 KB transferred, find out why before you add more.
- LCP image: discovered in the first HTML, sized for the slot, and not a multi-megabyte original. The responsive images guide is the method; the budget is the cap you refuse to exceed for that slot.
- Fonts: one or two families, subsetted, with a limited set of weights, preloaded only if they are truly on the critical path.
- Third parties: each one named, with an owner, and a default of zero on pages that do not need them. Analytics, if you have any, should be a conscious exception, not the first of six tags.
- Other first-party requests: as few as you can manage. Do not multiply a page into dozens of tiny CSS and icon files on a cold load if you can package the critical ones.
Write the numbers down next to a URL you will re-measure. The Performance Budget Calculator can help you convert “a 4G budget in seconds” into a rough byte cap, but the conversion is a model, not a promise about a real network.
If your current page is already twice these figures, do not set the starter numbers as a wall you will fail every day. Set a budget just below today’s size, and a second, lower target you intend to reach in a specific piece of work. A budget that is always red will be ignored.
Third parties, fonts and images
These three categories are where small sites usually blow a budget that their own HTML would have kept.
Third parties (chat, tags, embeds, A/B tests, maps, extra analytics) cost bytes, requests, and often main-thread time on every visit. Add them one at a time, measure them, and give them an expiry. A tag that was “temporary for a campaign” will still be there next year unless someone owns the budget line. If a third party will not run inside the budget, it does not ship, or it ships only on the page that needs it, after the main content.
Fonts look small in a design file and large on a cold load once you include italics, extra weights, and a second family for headings. Subset to the characters you use, prefer a system stack when the brand can bear it, and do not preload five files “just in case”.
Images are often the LCP element and the heaviest bytes on the page. A budget should cap the hero, and it should reject a gallery that dumps desktop originals into a phone-width column. Compression without srcset is only half of the work.
None of this requires a new aesthetic. It requires a no, or a not on this template.
How it relates to LCP, INP and CLS
A budget is a constraint on inputs. Core Web Vitals are outcomes in the field. They are related, and they are not the same thing.
- A JavaScript budget exists mainly to protect INP and, sometimes, LCP render delay, by keeping main-thread work in check.
- An image and discovery budget exists mainly to protect LCP.
- Font and injected-banner rules exist mainly to protect CLS, as well as LCP if a late font or banner changes what is painted.
You can pass a byte budget and still fail a vital, for example if the HTML is slow or the LCP image is discovered late. You can also fail a budget and still look green in the field on a fast-origin, low-interaction page, until the next widget arrives. Use the Core Web Vitals diagnosis method when an outcome fails, and use the budget so you are not diagnosing the same third party every quarter.
As of November 2026, the commonly cited “good” field thresholds remain those on web.dev. Confirm them there before you hard-code them into a report. I would still rather gate a build on bytes you control than on a field percentile you cannot see until after deploy.
Measuring and failing a build without theatre
Measure the built assets, on the templates you named, in continuous integration or in a release script:
- Fail the build if a bundle’s compressed size exceeds its cap. This is stable if you measure the same artefacts every time.
- Fail if a new third-party origin appears in a list you maintain, or if a page’s request count jumps by a threshold you chose.
- Be careful with Lighthouse-in-CI as a hard gate. Runs vary. If you use it, budget the underlying metrics with slack, pin the device profile, and do not fail on a one-point score dip. Theatre is a red build that the team retriggers until it goes green, without changing anything.
The Performance Budget Simulator is a place to rehearse “what if we add this script” before the script is in the repository. The gzip Size Estimator will tell you whether a source file is as cheap on the wire as it looks in the editor.
Record the measurement next to the commit. A budget nobody can audit is a slogan. This is the same instrumentation instinct as finding performance problems with Instruments: a number attached to a recording, not a feeling about the page.
Caching is not a substitute for a budget, but a good cache policy stops repeat visits from paying the cold-load cost again. The HTTP caching headers guide covers that part.
What to do when something will not fit
When a change breaks the budget, you have three honest options:
- Cut scope. Do not load the chat widget on articles. Subset the font. Replace a heavy carousel with static images. Split a tool so the library stays on the tool page.
- Pay for it elsewhere. If marketing must have a tag on the campaign landing page, take something off that page, or accept that the landing page has a looser budget than the articles, written down.
- Change the budget, in the same place you keep the original numbers, with a reason and a name. This is allowed. Doing it silently in a pull request that also adds the script is how the budget dies.
What you should not do is disable the check, or switch the measurement to an uncompressed figure that looks smaller, or test a thinner page than the one customers see.
If the work is genuinely valuable and too large, schedule a dedicated reduction (remove an old tag, split a bundle) before or with the addition. Do not hope that a later performance sprint will appear.
Reviewing the budget after launch
A budget should be reviewed when the site changes shape, not only when a check fails:
- After you add a new template (a store, a dashboard, a tool that needs a library).
- After a design change that alters the hero or the typography.
- When field Core Web Vitals worsen, or when a diagnosis points at a budget line you never had.
- On a calm quarterly pass, to tighten a target you have already beaten, or to drop a unit that is not telling you anything.
Write the review down next to the numbers. If the marketing site has become an application, the starter figures above will be wrong, and you will need budgets per template rather than one cap for the whole origin.
The point is not to preserve the first numbers for ever. The point is that any new number is still a decision.
Key takeaways
- A performance budget is a written limit you are willing to enforce, on specific templates and units, not a Lighthouse dashboard you glance at.
- Pick a few stable units — your own compressed JavaScript, the LCP image, fonts, named third parties — and measure them on the production build.
- Use a starter cap only as a starting point, set just below today’s size if you are already heavy, and do not treat any example figure as an official standard.
- Budgets constrain the inputs that usually affect LCP, INP and CLS; they do not replace diagnosing a field failure.
- When something will not fit, cut it, confine it, or change the budget on purpose. Review the numbers when the site changes shape.
If you want a second pair of eyes on a small site that has slowly outgrown its own pages, tell me about it.
Frequently asked questions
What should be in a performance budget?
A short list of limits you can measure: typically your own compressed JavaScript and CSS, the LCP image, font files, request counts, and each third-party tag by name. Name the templates the limits apply to, and say what happens when a change exceeds them. Field metrics are worth watching, but they are a poor only unit for a build gate.
How tight should a JavaScript budget be for a brochure site?
As tight as the site will still allow, and almost always tighter than a default framework install. I would start by measuring what you ship today, set a cap that stops it growing, and aim well under 150 KB transferred on a simple marketing template if you can. There is no official figure; a text-led site can live on much less, and should.
How do I enforce a performance budget in CI?
Measure the built artefacts and fail the build when a compressed bundle, a request list, or a named third party exceeds its cap. Those checks are stable. If you also run Lighthouse in CI, give the metrics slack and pin the test profile, so a noisy score cannot become theatre.
Is a performance budget the same as Core Web Vitals?
No. A budget is a constraint on what you ship. Core Web Vitals are field outcomes for loading, interactivity and visual stability. A good budget makes those outcomes easier to keep, but you can still fail a vital with a tidy byte count, and you still need a diagnosis when that happens.
How do third-party scripts affect a budget?
They spend the same bytes, requests and main-thread time as your own code, often on every page, and they are harder to trim later. Give each tag an owner and a line in the budget, load them only where they are needed, and measure them before you agree to add another.
Written by Lee Johnson. Published 10 November 2026.