All posts
PerformanceCI/CDPerformance Budgets

Website Performance Budgets: A Developer's Guide

Performance budgets set maximum constraints on page weight, timing, and request counts. Learn how to set them, enforce them in CI, and use them to prioritize work.

debugutils.dev

A performance budget is a set of constraints on the metrics that affect page speed. It defines the maximum allowed values for things like total page weight, number of requests, JavaScript size, or Lighthouse scores. When a change would exceed the budget, it gets flagged and either justified or reversed.

Performance budgets shift performance from a one-time optimization project to an ongoing constraint — part of how every feature is built.

Why Performance Budgets Work

Without a budget, performance degrades gradually. Each feature adds a bit of JavaScript. Each integration adds a third-party script. Each marketing request adds an image. No single change seems significant, but over six months the page is 40% heavier and users notice.

A budget makes performance visible. “This feature would push our JavaScript over budget” is a concrete constraint that teams can respond to. “This feature is slightly slower” is easy to dismiss.

Types of Performance Budgets

Quantity-based budgets

Hard limits on resource amounts:

Total page weight: < 500KB
JavaScript (total): < 150KB minified+gzipped
Images (initial load): < 200KB
HTTP requests: < 40
Third-party script domains: < 5

Quantity budgets are easy to measure and hard to game. For JavaScript specifically, see our guide on reducing JavaScript bundle size.

Timing-based budgets

Limits on performance metrics:

LCP: < 2.5s
FCP: < 1.8s
TTI: < 3.5s
TBT: < 200ms

Timing budgets directly reflect user experience but are harder to enforce automatically (they depend on test environment conditions).

Score-based budgets

Minimum Lighthouse scores:

Performance (mobile): ≥ 75
Accessibility: ≥ 85

Score budgets are easy to track but noisy — small environmental differences affect scores.

Setting Your Initial Budget

Start with where you are. Run debugutils.dev on your current pages and record the baseline metrics.

Set the budget 20% tighter. If your current JavaScript is 180KB, set the budget at 150KB. This creates pressure to reduce weight over time, not just maintain it. Use website speed benchmarks as a reference for what “good” looks like in your industry.

Prioritize the most impactful metrics. LCP and TBT affect Core Web Vitals and real user experience. Image and JS weight are quantity metrics that correlate with those timing metrics. Focus on these.

Be realistic. If you’re at LCP 4.5s, setting a budget of 2.0s immediately isn’t achievable in one sprint. Set a realistic target and tighten the budget gradually.

Enforcing Budgets in CI

Lighthouse CI budget assertions

In .lighthouserc.js:

module.exports = {
  ci: {
    assert: {
      assertions: {
        // Metric budgets
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'total-blocking-time': ['error', { maxNumericValue: 300 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],

        // Resource budgets
        'resource-summary:javascript:size': ['error', { maxNumericValue: 150000 }],
        'resource-summary:image:size': ['error', { maxNumericValue: 300000 }],
        'resource-summary:total:size': ['error', { maxNumericValue: 600000 }],
        'resource-summary:third-party:count': ['warn', { maxNumericValue: 5 }],
      },
    },
  },
};

The build fails if any budget is exceeded. This creates a forcing function: the developer must either optimize or get explicit sign-off to increase the budget.

Bundle size tracking

For JavaScript specifically, use bundlesize or size-limit:

// package.json
{
  "bundlesize": [
    { "path": "./dist/assets/*.js", "maxSize": "100 kB" },
    { "path": "./dist/assets/*.css", "maxSize": "30 kB" }
  ]
}

This runs independently of Lighthouse and catches JavaScript regressions before they even get to a Lighthouse run. For the full enforcement pipeline, see our guide on automating speed testing.

Communicating Budgets to Stakeholders

Performance budgets work best when the whole team understands and agrees on them. Frame them in user terms:

“Our performance budget ensures that our pages load the main content in under 2.5 seconds on a typical mobile connection. Exceeding this budget means we’re delivering a worse experience to users — and our Core Web Vitals will suffer, which affects our SEO.”

When a new feature request would exceed the budget, the conversation becomes: is this feature worth the performance cost? Can we find a lighter alternative? Can we remove something else to make room?

Measure and Review

Set a recurring calendar reminder — monthly — to review your performance budgets:

  1. Run debugutils.dev on key pages
  2. Compare metrics to budget thresholds
  3. Identify trends (are you moving toward or away from the budget?)
  4. Tighten budgets where you have headroom
  5. Prioritize optimization work where you’re closest to or over budget