---
title: "Performance Budgets with Lighthouse CI: Assertions & budget.json"
description: "Set and enforce performance budgets with Lighthouse CI. Configure assertions, presets, custom thresholds, and budget.json to prevent regressions in CI/CD."
canonical_url: "https://unlighthouse.dev/learn-lighthouse/lighthouse-ci/budgets"
last_updated: "2026-07-05T10:28:38.110Z"
---

Performance budgets enforce web performance standards in your CI pipeline. Lighthouse CI fails builds when metrics exceed thresholds, preventing performance regressions from reaching production.

## Why Performance Budgets Matter

Performance budgets translate business requirements into measurable technical constraints:

- **Prevent regressions**: Catch performance degradation before deployment
- **Objective standards**: Replace subjective "feels slow" with measurable thresholds
- **Team accountability**: Make performance a first-class concern alongside features
- **User experience**: Maintain fast load times as codebases grow

Without budgets, performance degrades incrementally. A 50KB script here, an unoptimized image there - each change seems minor, but compound effects destroy user experience.

### The Business Case

Research shows direct revenue impact from performance:

<table>
<thead>
  <tr>
    <th>
      Finding
    </th>
    
    <th>
      Source
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        0.1s improvement = 8.4% retail conversion increase
      </strong>
    </td>
    
    <td>
      <a href="https://web.dev/case-studies/vitals-business-impact" rel="nofollow">
        Google's research on the business impact of Core Web Vitals
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Every 100ms costs Amazon 1% in sales
      </strong>
    </td>
    
    <td>
      <a href="https://www.cloudflare.com/en-au/learning/performance/more/website-performance-conversion-rates/" rel="nofollow">
        Cloudflare
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        BBC loses 10% of users per additional second
      </strong>
    </td>
    
    <td>
      <a href="https://web.dev/learn/performance/why-speed-matters" rel="nofollow">
        Google Research
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Bounce probability increases 90% as load goes 1s→5s
      </strong>
    </td>
    
    <td>
      <a href="https://web.dev/learn/performance/why-speed-matters" rel="nofollow">
        Google Research
      </a>
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Good LCP = 61% conversion increase for Rakuten
      </strong>
    </td>
    
    <td>
      <a href="https://web.dev/case-studies/rakuten" rel="nofollow">
        web.dev Case Study
      </a>
    </td>
  </tr>
</tbody>
</table>

### The Page Weight Problem

[Page weight has grown significantly over the last decade](https://almanac.httparchive.org/en/2024/page-weight) (484KB → 2.2MB average). The [2024 Web Almanac](https://almanac.httparchive.org/en/2024/page-weight) reports median page weight at 2,675 KB (+8% YoY) with median JavaScript at 558 KB on mobile.

[Alex Russell's research](https://almanac.httparchive.org/en/2024/performance) suggests a **365 KB JavaScript budget** for sub-3-second loads on typical mobile devices - yet the 75th percentile exceeds 650 KB. Before setting budgets, measure your current baseline with the [Page Size Checker](/tools/page-size).

## Two Approaches to Budgets

Lighthouse CI supports two budget methods:

### 1. Assertions in lighthouserc.js

Define budgets directly in your Lighthouse CI configuration:

```js
// lighthouserc.js
export default {
  ci: {
    assert: {
      preset: 'lighthouse:recommended',
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
      }
    }
  }
}
```

Best for simple budgets integrated with CI configuration.

### 2. Separate budget.json

Use Lighthouse's native budget format:

```json
// budget.json
[
  {
    "path": "/*",
    "resourceSizes": [
      { "resourceType": "script", "budget": 300 },
      { "resourceType": "image", "budget": 500 }
    ],
    "timings": [
      { "metric": "interactive", "budget": 3000 }
    ]
  }
]
```

Reference it in lighthouserc.js:

```js
const config2 = {
  ci: {
    assert: {
      budgetsFile: './budget.json'
    }
  }
}
```

Best for complex budgets, especially multiple URL patterns or resource-level constraints.

## Assertion Presets

Lighthouse CI includes three presets based on common use cases.

### lighthouse:all

Strictest preset - every audit must pass:

```js
const config3 = {
  ci: {
    assert: {
      preset: 'lighthouse:all'
    }
  }
}
```

Enforces all 100+ Lighthouse audits. Extremely difficult to pass. Use for greenfield projects with aggressive performance goals.

### lighthouse:recommended

Balanced preset - focuses on critical metrics:

```js
const config4 = {
  ci: {
    assert: {
      preset: 'lighthouse:recommended'
    }
  }
}
```

Behavior:

- **Errors** when accessibility, best practices, or SEO audits fail (perfect scores required)
- **Warns** when performance metrics fall below a score of 90
- Covers critical audits without being overly strict on performance variance

Most common starting point. Reasonable for production sites.

### lighthouse:no-pwa

Recommended preset without PWA requirements:

```js
const config5 = {
  ci: {
    assert: {
      preset: 'lighthouse:no-pwa'
    }
  }
}
```

Same as `lighthouse:recommended` but excludes Progressive Web App audits. Use for standard websites that don't need PWA capabilities.

## Assertion Syntax

Lighthouse CI uses ESLint-style assertion levels.

### Assertion Levels

Three severity levels control build status:

```js
const config6 = {
  assertions: {
    'first-contentful-paint': 'off', // Ignore this audit
    'largest-contentful-paint': 'warn', // Log warning, don't fail build
    'cumulative-layout-shift': 'error' // Fail build if threshold exceeded
  }
}
```

- **off**: Skip assertion entirely
- **warn**: Report violations without failing build
- **error**: Fail build on violations (default for preset assertions)

### minScore vs maxNumericValue

Two threshold types handle different metric formats:

**minScore** for 0-1 scores (categories, some audits):

```js
const config7 = {
  assertions: {
    'categories:performance': ['error', { minScore: 0.9 }],
    'categories:accessibility': ['error', { minScore: 0.95 }],
    'categories:seo': ['warn', { minScore: 0.8 }]
  }
}
```

Score must meet or exceed threshold. 0.9 means 90/100 in Lighthouse reports.

**maxNumericValue** for millisecond/byte values:

```js
const config8 = {
  assertions: {
    'first-contentful-paint': ['error', { maxNumericValue: 1800 }],
    'speed-index': ['error', { maxNumericValue: 3000 }],
    'total-byte-weight': ['warn', { maxNumericValue: 1500000 }]
  }
}
```

Value must be at or below threshold. FCP example allows up to 1800ms.

## Aggregation Methods

Lighthouse CI runs multiple audits per URL. Aggregation determines which run's value to compare against budgets.

```js
const config9 = {
  ci: {
    assert: {
      assertions: { /* ... */ }
    },
    collect: {
      numberOfRuns: 5
    },
    upload: {
      target: 'temporary-public-storage'
    }
  }
}
```

Four aggregation strategies:

### optimistic (default)

Uses best (fastest) value from sorted runs. Presets like `lighthouse:recommended` use this by default.

### median

Uses middle value from sorted runs:

```js
const config10 = {
  assert: {
    assertMatrix: [{ matchingUrlPattern: '.*', assertions: { /* ... */ } }],
    // median is default, explicit:
    preset: 'lighthouse:recommended'
  }
}
```

Most stable - ignores outliers. Best for typical use when you want consistent results.

### pessimistic

Uses worst (slowest) run:

```js
const config11 = {
  assert: {
    assertions: {
      'largest-contentful-paint': ['error', {
        maxNumericValue: 2500,
        aggregationMethod: 'pessimistic'
      }]
    }
  }
}
```

Strictest - makes sure worst case meets budget. Good for critical paths.

### median-run

Uses all values from the median run (not per-metric median):

```js
const config12 = {
  assert: {
    assertions: {
      interactive: ['error', {
        maxNumericValue: 3500,
        aggregationMethod: 'median-run'
      }]
    }
  }
}
```

Checks that metric correlations are preserved. Use when metrics interrelate.

## Category Assertions

Category assertions set thresholds for Lighthouse's five main scores.

```js
const config13 = {
  assertions: {
    'categories:performance': ['error', { minScore: 0.9 }],
    'categories:accessibility': ['error', { minScore: 0.95 }],
    'categories:best-practices': ['error', { minScore: 0.9 }],
    'categories:seo': ['error', { minScore: 0.9 }],
    'categories:pwa': ['warn', { minScore: 0.5 }]
  }
}
```

Categories aggregate multiple audits. Performance category includes [FCP](/glossary/fcp), [LCP](/learn-lighthouse/lcp), [TBT](/glossary/tbt), [CLS](/learn-lighthouse/cls), and [Speed Index](/glossary/speed-index).

Use categories for high-level budgets. Override with individual audit assertions for granular control.

## Individual Audit Assertions

Target specific Lighthouse audits for precise budgets.

### Core Web Vitals

```js
const config14 = {
  assertions: {
    'first-contentful-paint': ['error', { maxNumericValue: 1800 }],
    'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
    'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
    'total-blocking-time': ['error', { maxNumericValue: 200 }],
    'speed-index': ['error', { maxNumericValue: 3400 }]
  }
}
```

Align with Google's [Core Web Vitals](/learn-lighthouse/core-web-vitals) thresholds for good user experience. See detailed guides for [LCP](/learn-lighthouse/lcp), [CLS](/learn-lighthouse/cls), and [INP](/learn-lighthouse/inp) optimization.

<warning>

**Lab scores ≠ field performance**: [Analysis of real-world CrUX data](https://web.dev/articles/lab-and-field-data-differences) found that a significant portion of pages scoring high in Lighthouse may still fail one or more Core Web Vitals thresholds in real-world conditions. Use [RUM monitoring](/learn-lighthouse/core-web-vitals) alongside synthetic testing.

</warning>

### Resource Metrics

```js
const config15 = {
  assertions: {
    'total-byte-weight': ['error', { maxNumericValue: 1600000 }], // 1.6MB
    'dom-size': ['warn', { maxNumericValue: 1500 }],
    'bootup-time': ['warn', { maxNumericValue: 3500 }],
    'mainthread-work-breakdown': ['warn', { maxNumericValue: 4000 }]
  }
}
```

Control page weight and computational cost. High [total byte weight](/learn-lighthouse/lcp/total-byte-weight) directly impacts load performance.

### Resource Budgets by Type

```js
const config16 = {
  assertions: {
    'resource-summary:script:size': ['error', { maxNumericValue: 300000 }], // 300KB JS
    'resource-summary:stylesheet:size': ['error', { maxNumericValue: 100000 }], // 100KB CSS
    'resource-summary:image:size': ['warn', { maxNumericValue: 500000 }], // 500KB images
    'resource-summary:font:size': ['warn', { maxNumericValue: 100000 }], // 100KB fonts
    'resource-summary:total:size': ['error', { maxNumericValue: 1500000 }] // 1.5MB total
  }
}
```

Granular control over asset budgets. Prevents JavaScript bloat from [unused JavaScript](/learn-lighthouse/lcp/unused-javascript) or [unminified scripts](/learn-lighthouse/lcp/unminified-javascript).

<note type="info">

**Recommended JS budget**: [Alex Russell's performance inequality research](https://almanac.httparchive.org/en/2024/performance) suggests 365 KB total JavaScript for sub-3-second loads on median mobile devices. The 300KB budget above is aggressive but achievable for content sites.

</note>

### Accessibility Audits

```js
const config17 = {
  assertions: {
    'color-contrast': 'error',
    'image-alt': 'error',
    'label': 'error',
    'link-name': 'error',
    'tabindex': 'warn'
  }
}
```

Binary pass/fail audits don't need thresholds. See the [accessibility guide](/learn-lighthouse/accessibility) for complete audit details.

## budget.json Format

Lighthouse's native budget format supports per-path budgets.

### Basic Structure

```json
[
  {
    "path": "/*",
    "resourceSizes": [
      {
        "resourceType": "script",
        "budget": 300
      },
      {
        "resourceType": "stylesheet",
        "budget": 50
      },
      {
        "resourceType": "image",
        "budget": 500
      },
      {
        "resourceType": "font",
        "budget": 100
      },
      {
        "resourceType": "total",
        "budget": 1500
      }
    ],
    "timings": [
      {
        "metric": "first-contentful-paint",
        "budget": 2000
      },
      {
        "metric": "largest-contentful-paint",
        "budget": 2500
      },
      {
        "metric": "interactive",
        "budget": 3500
      },
      {
        "metric": "total-blocking-time",
        "budget": 200
      }
    ]
  }
]
```

### Resource Types

Available `resourceType` values:

- `script` - JavaScript bundles
- `stylesheet` - CSS files
- `image` - All image formats
- `media` - Video/audio
- `font` - Web fonts
- `document` - HTML documents
- `other` - Everything else
- `third-party` - External domain resources
- `total` - Combined size

### Timing Metrics

Available `metric` values:

- `first-contentful-paint`
- `largest-contentful-paint`
- `interactive`
- `total-blocking-time`
- `cumulative-layout-shift`
- `max-potential-fid`
- `speed-index`

### Multiple URL Budgets

Define different budgets per page type:

```json
[
  {
    "path": "/",
    "resourceSizes": [
      { "resourceType": "script", "budget": 400 },
      { "resourceType": "total", "budget": 2000 }
    ]
  },
  {
    "path": "/blog/*",
    "resourceSizes": [
      { "resourceType": "script", "budget": 200 },
      { "resourceType": "image", "budget": 800 }
    ]
  },
  {
    "path": "/app/*",
    "resourceSizes": [
      { "resourceType": "script", "budget": 600 },
      { "resourceType": "total", "budget": 1800 }
    ]
  }
]
```

Match patterns use glob syntax. First matching path applies.

### Using budget.json

Reference budget file in lighthouserc.js:

```js
const config18 = {
  ci: {
    assert: {
      budgetsFile: './budget.json'
    },
    collect: {
      url: [
        'http://localhost:3000/',
        'http://localhost:3000/blog/post-1',
        'http://localhost:3000/app/dashboard'
      ]
    }
  }
}
```

Budget.json takes precedence over assertion configuration.

## Combining Presets with Overrides

Start with preset, override specific assertions:

```js
const config19 = {
  ci: {
    assert: {
      preset: 'lighthouse:no-pwa',
      assertions: {
        // Relax performance requirement
        'categories:performance': ['warn', { minScore: 0.85 }],

        // Stricter Core Web Vitals
        'largest-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.05 }],

        // Resource budgets
        'resource-summary:script:size': ['error', { maxNumericValue: 350000 }],
        'resource-summary:total:size': ['error', { maxNumericValue: 1800000 }],

        // Disable problematic audits
        'uses-http2': 'off',
        'redirects': 'off'
      }
    }
  }
}
```

Presets provide baseline. Overrides customize for your application.

## Setting Realistic Budgets

Don't guess budgets. Base them on current performance:

### 1. Run Lighthouse CI Locally

```bash
lhci collect --url=http://localhost:3000
lhci assert --preset=lighthouse:recommended
```

Review failures to see current metric values.

### 2. Set Budgets Above Current

Add 10-20% buffer to current values:

```js
const config20 = {
  // Current FCP: 1600ms
  // Budget: 1600 * 1.15 = 1840ms
  'first-contentful-paint': ['error', { maxNumericValue: 1840 }]
}
```

Prevents immediate failures while preventing regressions.

### 3. Tighten Over Time

Gradually reduce budgets as performance improves:

```js
const config21 = {
  // Week 1
  'largest-contentful-paint': ['warn', { maxNumericValue: 3000 }]
}

const config22 = {
  // Week 4
  'largest-contentful-paint': ['warn', { maxNumericValue: 2500 }]
}

const config23 = {
  // Week 8
  'largest-contentful-paint': ['error', { maxNumericValue: 2500 }]
}
```

Move from warn to error as team adapts.

## Practical Examples

### Marketing Site

```js
const config24 = {
  ci: {
    assert: {
      preset: 'lighthouse:no-pwa',
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.95 }],
        'categories:seo': ['error', { minScore: 0.95 }],

        'first-contentful-paint': ['error', { maxNumericValue: 1500 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.05 }],

        'resource-summary:script:size': ['error', { maxNumericValue: 250000 }],
        'resource-summary:image:size': ['warn', { maxNumericValue: 600000 }]
      }
    }
  }
}
```

Priorities: [SEO](/learn-lighthouse/seo), [accessibility](/learn-lighthouse/accessibility), fast [LCP](/learn-lighthouse/lcp) for conversions.

### SaaS Application

```js
const config25 = {
  ci: {
    assert: {
      preset: 'lighthouse:no-pwa',
      assertions: {
        'categories:performance': ['warn', { minScore: 0.8 }],
        'categories:accessibility': ['error', { minScore: 0.9 }],

        'interactive': ['error', { maxNumericValue: 3500 }],
        'total-blocking-time': ['error', { maxNumericValue: 300 }],

        'resource-summary:script:size': ['warn', { maxNumericValue: 500000 }],
        'bootup-time': ['warn', { maxNumericValue: 4000 }]
      }
    }
  }
}
```

Priorities: Interactivity, reasonable bundle size for complex UI.

### Content Site

```js
const config26 = {
  ci: {
    assert: {
      budgetsFile: './budget.json'
    }
  }
}
```

```json
[
  {
    "path": "/",
    "resourceSizes": [
      { "resourceType": "script", "budget": 200 },
      { "resourceType": "image", "budget": 400 },
      { "resourceType": "total", "budget": 1200 }
    ],
    "timings": [
      { "metric": "first-contentful-paint", "budget": 1500 },
      { "metric": "largest-contentful-paint", "budget": 2000 }
    ]
  },
  {
    "path": "/article/*",
    "resourceSizes": [
      { "resourceType": "script", "budget": 150 },
      { "resourceType": "image", "budget": 800 },
      { "resourceType": "total", "budget": 1500 }
    ],
    "timings": [
      { "metric": "first-contentful-paint", "budget": 1200 },
      { "metric": "largest-contentful-paint", "budget": 1800 }
    ]
  }
]
```

Priorities: Fast content delivery, article images allowed, minimal JavaScript.

### E-commerce Site

```js
const config27 = {
  module: {
    exports: {
      ci: {
        assert: {
          preset: 'lighthouse:no-pwa',
          assertions: {
            'categories:performance': ['error', { minScore: 0.85 }],
            'categories:accessibility': ['error', { minScore: 0.9 }],

            'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
            'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
            'total-blocking-time': ['warn', { maxNumericValue: 350 }],

            'resource-summary:script:size': ['warn', { maxNumericValue: 400000 }],
            'resource-summary:image:size': ['error', { maxNumericValue: 1000000 }],
            'resource-summary:third-party:size': ['warn', { maxNumericValue: 300000 }]
          }
        }
      }
    }
  }
}
```

Priorities: [LCP](/learn-lighthouse/lcp) (product images), [CLS](/learn-lighthouse/cls) (prevent layout shifts during checkout), third-party control (analytics/ads).

## When Budgets Fail

Budget violations point to specific performance problems. These diagnostic guides help identify root causes and implement fixes:

**Core Web Vitals Failures**

- [Largest Contentful Paint (LCP)](/learn-lighthouse/lcp) - Slow loading of main content

  - [Total byte weight](/learn-lighthouse/lcp/total-byte-weight) - Excessive page resources
  - [Unused JavaScript](/learn-lighthouse/lcp/unused-javascript) - Dead code bloating bundles
  - [Unminified JavaScript](/learn-lighthouse/lcp/unminified-javascript) - Missing compression
- [Cumulative Layout Shift (CLS)](/learn-lighthouse/cls) - Visual instability and layout shifts
- [Interaction to Next Paint (INP)](/learn-lighthouse/inp) - Poor interactivity and responsiveness

**Category Score Failures**

- [Accessibility](/learn-lighthouse/accessibility) - WCAG violations, missing labels, color contrast
- [SEO](/learn-lighthouse/seo) - Meta tags, structured data, mobile usability
- [Best Practices](/learn-lighthouse/best-practices) - Security, browser compatibility, console errors

**Resource Budget Failures**

- JavaScript size violations: Check [unused JavaScript](/learn-lighthouse/lcp/unused-javascript) and [unminified JavaScript](/learn-lighthouse/lcp/unminified-javascript)
- Total size violations: Review [total byte weight](/learn-lighthouse/lcp/total-byte-weight) optimization strategies
- Image budgets: Optimize formats, implement responsive images, lazy loading

Start with [Core Web Vitals](/learn-lighthouse/core-web-vitals) guide for prioritized optimization strategies.

## Next Steps

- [Configuration](/learn-lighthouse/lighthouse-ci/configuration) - Full lighthouserc.js options
- [GitHub Actions](/learn-lighthouse/lighthouse-ci/github-actions) - Automate budgets in CI
- [GitLab CI](/learn-lighthouse/lighthouse-ci/gitlab-ci) - GitLab CI integration
tion
