Skip to main content

Set Performance Budgets for Web Apps in 2026

Set Performance Budgets for Web Apps in 2026

A performance budget is a limit you set on a measurable property of your pages, such as total JavaScript size, the number of third-party requests or the Largest Contentful Paint time, and then enforce on every change. MDN defines it as “a limit to prevent regressions” (developer.mozilla.org, read 2026-10-23). The point is not the number itself but the check: a budget that is not tested in CI is a wish.

This guide covers how to choose budget metrics, where the thresholds come from, and how to enforce them with Lighthouse CI, including a config I ran. For how to pick performance targets in the first place (including back-end response times under load), see the guide to setting performance benchmarks for web applications.

Key takeaways

  • Use three kinds of limit: quantity (bytes and request counts), timing (LCP, TBT, FCP) and rule-based (a Lighthouse score). Quantity budgets are the most stable in CI because they do not move with machine noise.
  • Google’s Core Web Vitals targets are LCP 2.5 s, INP 200 ms and CLS 0.1, measured at the 75th percentile of real page loads (web.dev, last updated 2024-10-31). Those are field numbers. A lab budget in CI approximates them.
  • Give each budget a warning level and an error level. Warnings track debt, errors block the merge.
  • Gate with Lighthouse CI assertions. I ran an example below and a violated budget made lhci autorun exit with status 1.
  • Do not copy another site’s budget numbers. Derive yours from your own baseline and your users’ devices.

What types of performance budget are there?

MDN groups budgets into timing-based (for example Time to Interactive or First Contentful Paint), quantity-based (for example the amount of JavaScript or total image size) and rule-based (for example a Lighthouse score) (developer.mozilla.org, read 2026-10-23). Pick at least one of each, because they catch different problems:

TypeExample limit (illustrative)Catches
QuantityJavaScript under 170 KB, 10 third-party requestsA dependency or an image added without anyone noticing
TimingLCP under 2.5 s in the lab runA slow server response, a late hero image, render-blocking CSS
Rule-basedLighthouse performance score of at least 0.9A broad regression with no single cause

The example limits are illustrations to show the format, not recommendations. No standard says a page should have 170 KB of JavaScript.

Which metrics should a budget use?

Start from Core Web Vitals, because they are the metrics Google reports. The thresholds are LCP within 2.5 seconds, INP at 200 milliseconds or less and CLS at 0.1 or less, and a page passes when it meets all three at the 75th percentile of page loads, segmented by mobile and desktop (web.dev/articles/vitals, last updated 2024-10-31, read 2026-10-23).

Two cautions for the lab. First, a Lighthouse run is a single synthetic load, so it cannot produce a 75th percentile or measure INP the way real users interact. Lighthouse’s own performance score in version 13.5.0 weights FCP at 10, Speed Index at 10, LCP at 25, Total Blocking Time at 30 and CLS at 25, with INP at weight 0 (read from the package’s default config, 2026-10-23); Total Blocking Time is the lab metric teams usually watch for responsiveness. Second, Time to Interactive, which older budget guides and MDN’s example still use, is weighted 0 in that same config and sits in a hidden group, so do not build a new budget on it.

The practical split: set lab budgets on LCP, CLS and TBT plus resource sizes for the merge gate, and check Core Web Vitals at the 75th percentile in field data and in scheduled monitoring. The glossary entry on Core Web Vitals and the performance budget entry have short definitions.

How do you choose the numbers?

Measure first. Run the page several times, record the metrics and the resource sizes, and set the first budget at or just above today’s value so the check passes and then ratchets down. MDN’s advice is to identify the devices and connection speeds your users have, for example “a ~$200 Android device over a 3G connection”, and to convert time goals into file-size budgets from there (developer.mozilla.org, read 2026-10-23).

For bundle-size budgets, work backwards from a time goal: how much JavaScript can a mid-range phone download, parse and run inside your LCP and TBT limits? Test it by running the page with CPU and network throttling and moving the number until the timing budget holds. The mobile-first performance testing guide covers why the device matters more than the desktop result.

Use two levels, as MDN recommends: a warning level that lets you plan debt without blocking deploys, and an error level that is “an upper bound limit” you do not cross. A rise of a few kilobytes should warn; a doubling should fail.

How do you enforce a budget in CI with Lighthouse CI?

Lighthouse CI (@lhci/cli) runs Lighthouse several times against your pages and checks the results against assertions in a lighthouserc file. Each assertion has a level: off, warn (printed, exit code stays zero) or error (printed, non-zero exit) (googlechrome.github.io/lighthouse-ci, read 2026-10-23). Numeric assertions take maxNumericValue (milliseconds or bytes), scores take minScore, and resource sizes use the resource-summary:<type>:size form, in bytes.

I ran exactly this config with @lhci/cli 0.15.1 (Lighthouse 13.5.0) against a tiny static page on 2026-10-23, and it passed with exit code 0. The limits are illustrative:

{
  "ci": {
    "collect": { "staticDistDir": "./dist", "numberOfRuns": 3 },
    "assert": {
      "assertions": {
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        "total-blocking-time": ["warn", { "maxNumericValue": 200 }],
        "resource-summary:script:size": ["error", { "maxNumericValue": 174080 }],
        "resource-summary:third-party:count": ["warn", { "maxNumericValue": 10 }]
      }
    }
  }
}

Run npm install -g @lhci/cli@0.15.x and lhci autorun in a CI step (the getting-started page pins 0.15.x). For a site served elsewhere, replace staticDistDir with a url list. In a second run I changed the script limit to 1 byte on a page with a 370-byte script, and the run printed resource-summary.script.size failure for maxNumericValue assertion, expected: <=1, found: 370 and exited with status 1, which is what fails the CI job. With the real limits the run exits 0.

Two details matter in practice. The default aggregationMethod is optimistic, meaning the best of your runs is checked, so a gate that runs three times and passes if any run is under the limit can hide a real regression; use median or pessimistic for the gate and expect noise on timing metrics (the docs list the options). And assertions can be scoped per page with assertMatrix, so a heavy dashboard can have different limits from a landing page.

What about a budget.json file and --budget-path?

Lighthouse used to read a budget.json file through a --budget-path flag, and the 2019 web.dev article (“Use Lighthouse for performance budgets”) still describes it. I installed Lighthouse 13.5.0 from npm on 2026-10-23 and found no budget code in its source or CLI help, so do not rely on that flag. Lighthouse CI still accepts a budgets file: set "assert": { "budgetsFile": "./budget.json" } and it converts the file into assertions. That option cannot be combined with other assertion options. The file format is a JSON array with path, resourceSizes (in KB), resourceCounts and timings (in ms). I ran one with a script budget of 0.1 KB and it failed as expected, reporting a limit of 102.4 bytes.

[
  {
    "path": "/*",
    "resourceSizes": [{ "resourceType": "script", "budget": 170 }],
    "resourceCounts": [{ "resourceType": "third-party", "budget": 10 }],
    "timings": [{ "metric": "largest-contentful-paint", "budget": 2500 }]
  }
]

Bundler-level limits are a good second layer: webpack can warn when assets exceed a size limit, and Bundlesize runs file-size checks in CI (both listed on MDN, read 2026-10-23). They fail earlier than a browser run and name the offending file.

How do you keep a budget honest after launch?

A lab check on a pull request tells you a change did not make the build worse. It does not tell you what real users see, which varies with device, network and third-party behaviour. Keep two loops: the CI gate for regressions, and scheduled measurement of the live pages for drift.

LoadFocus’s page speed monitoring runs scheduled Lighthouse audits and Core Web Vitals checks, with schedules from hourly to weekly, and the page states you can “set Core Web Vitals budgets and get alerted the moment a metric regresses past Google’s recommended thresholds”, with alerts to email, Slack or Microsoft Teams (loadfocus.com/page-speed-monitoring, read 2026-10-23). It also shows Chrome UX Report data for real visitors. Third-party scripts deserve their own count limit in both loops, since a tag manager change can break a budget without a code deploy. The DevOps toolchain guide covers where these checks sit in a pipeline.

What mistakes break performance budgets?

  • Setting it and forgetting it. Review limits when the product, the audience devices or Google’s thresholds change.
  • Budgets that always pass. An optimistic aggregation, a limit far above the current value or a check that only warns will never stop anything. Prove the gate by breaking it once.
  • Gating on noisy metrics only. A single timing run varies. Back timing limits with size limits, which are stable.
  • Copying numbers. A budget from a blog post fits a different site. Use your own baseline.
  • Gaming the number. Deferring everything past the measured moment satisfies the metric and hurts the user. Check the page by eye as well.
  • One budget for every page. A checkout and a docs page do not need the same limits; scope them.

Frequently Asked Questions

What is a performance budget?

A performance budget is a limit on a measurable property of a page, such as JavaScript size, request count, LCP or a Lighthouse score, that is checked on every change so performance does not regress. MDN describes it as a limit to prevent regressions.

What are the Core Web Vitals thresholds?

Google’s targets are LCP within 2.5 seconds, INP of 200 milliseconds or less and CLS of 0.1 or less, at the 75th percentile of page loads, segmented by mobile and desktop (web.dev, last updated 2024-10-31).

How do I enforce a performance budget in CI?

Add Lighthouse CI to the pipeline with lhci autorun, define assertions in lighthouserc.json at the error level, and let the job fail on a non-zero exit. Use a median or pessimistic aggregation for timing metrics, and add bundle-size checks as a second layer.

Does Lighthouse still support --budget-path?

I could not find it in Lighthouse 13.5.0 (checked on 2026-10-23), although older guides document it. Lighthouse CI accepts a budget.json through its budgetsFile option instead.

What is a good JavaScript size budget?

There is no standard number. Derive it from the devices and networks of your users and your timing goals, and start from your current value. Any figure in an article, including the ones here, is an example of the format.

Should budgets use Time to Interactive?

Not for a new budget. Lighthouse 13.5.0 gives Time to Interactive no weight in the performance score and lists it as a hidden metric. Use LCP, CLS and Total Blocking Time in the lab, and INP in field data.

Sources

  • MDN, Performance budgets (developer.mozilla.org/en-US/docs/Web/Performance/Guides/Performance_budgets, read 2026-10-23).
  • web.dev, Web Vitals (web.dev/articles/vitals, last updated 2024-10-31, read 2026-10-23).
  • web.dev, Use Lighthouse for performance budgets (2019-06-14, read 2026-10-23).
  • Lighthouse CI documentation: getting started and configuration (googlechrome.github.io/lighthouse-ci, read 2026-10-23); own run with @lhci/cli 0.15.1 and Lighthouse 13.5.0, 2026-10-23.
  • LoadFocus page speed monitoring page (loadfocus.com/page-speed-monitoring, read 2026-10-23).

Chris

Head of Content at LoadFocus

Chris leads content and oversees development across the services this blog covers. The guides and tool comparisons here come out of the same decisions that shape what ships.

Weekly tips right in your inbox.

Don’t miss out on the latest testing tips and tricks, tools and news related to software testing.

Already +20,000 members in the list.

The Load Testing Platform with AI That Explains Your Results

Load test websites and APIs from 25 cloud regions, monitor page speed and uptime, and get AI analysis that explains your results in plain English.

Cloud load testing, website speed and API monitoring in one place.

Hi there! Can I share a cool thing I'm working on with you?

A software testing newsletter that sparks joy.

Our goal is to create helpful content for software developers and testers.

If you're not into it, you can unsubscribe instantly.