Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
TestingMedium

Enforce performance budgets in CI

Define measurable performance thresholds (bundle size, Lighthouse scores, Core Web Vitals) and fail CI builds automatically when they're exceeded.

Utilities
Quick take
Typical fix time 25 min
  • Define specific thresholds: max bundle size, min Lighthouse score, max LCP time
  • Fail CI when budgets are exceeded to prevent performance regressions
  • Size budgets catch accidental heavy dependencies; Lighthouse budgets catch runtime regressions
  • Start with loose budgets and tighten them as you optimize
  • Monitor production RUM so regressions are caught after deploy, not only in CI
Why it matters: Without automated enforcement, performance degrades gradually — each PR adds a small library, each feature adds a few KB, and within months the app that loaded in 2 seconds now takes 5. Performance budgets make regressions visible at PR time rather than after user complaints. Catching 'this PR added 200KB to the bundle' in review is far cheaper than debugging a slow production site.

Rule Details

Performance budgets are explicit constraints on measurable metrics that fail your build when exceeded.

Code Examples

pnpm add -D size-limit @size-limit/preset-app
// package.json
{
  "size-limit": [
    {
      "name": "Main bundle",
      "path": "dist/assets/index-*.js",
      "limit": "200 kB",
      "gzip": true
    },
    {
      "name": "CSS",
      "path": "dist/assets/index-*.css",
      "limit": "30 kB",
      "gzip": true
    },
    {
      "name": "Vendor chunk",
      "path": "dist/assets/vendor-*.js",
      "limit": "150 kB",
      "gzip": true
    }
  ],
  "scripts": {
    "size": "size-limit",
    "analyze": "size-limit --why"
  }
}
# .github/workflows/size-check.yml
- name: Check bundle size
  run: |
    pnpm build
    npx size-limit

Why It Matters

Without automated enforcement, performance degrades gradually — each PR adds a small library, each feature adds a few KB, and within months the app that loaded in 2 seconds now takes 5. Performance budgets make regressions visible at PR time rather than after user complaints. Catching 'this PR added 200KB to the bundle' in review is far cheaper than debugging a slow production site.

Lighthouse CI

pnpm add -D @lhci/cli
// lighthouserc.json
{
  "ci": {
    "collect": {
      "url": ["http://localhost:3000", "http://localhost:3000/about"],
      "numberOfRuns": 3
    },
    "assert": {
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.8 }],
        "categories:accessibility": ["error", { "minScore": 0.9 }],
        "first-contentful-paint": ["warn", { "maxNumericValue": 2000 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "total-blocking-time": ["error", { "maxNumericValue": 300 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}
# .github/workflows/lighthouse.yml
- name: Run Lighthouse CI
  run: |
    pnpm build
    npx lhci autorun
// budget.json
[
  {
    "path": "/*",
    "resourceSizes": [
      { "resourceType": "script", "budget": 250 },
      { "resourceType": "image", "budget": 500 },
      { "resourceType": "total", "budget": 1600 }
    ]
  }
]

Webpack/Vite Build Budgets

// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        // Warn when chunks are large
        manualChunks: {
          vendor: ['react', 'react-dom'],
          ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu']
        }
      }
    },
    chunkSizeWarningLimit: 500 // KB — build warns above this
  }
}
MetricGoodNeeds Work
Main JS bundle (gzipped)< 150 KB> 300 KB
Total JS (gzipped)< 400 KB> 800 KB
Lighthouse Performance> 80< 60
Largest Contentful Paint< 2.5s> 4s
Total Blocking Time< 200ms> 600ms
Cumulative Layout Shift< 0.1> 0.25

Progressive Tightening

  • Start with thresholds the current app can realistically meet.
  • Promote stable warnings to blocking errors once the team fixes obvious regressions.
  • Revisit budgets after major architectural changes so they remain intentional instead of inherited.

Production RUM Completes the Loop

CI budgets catch regressions before merge, but field data catches regressions caused by real devices, third-party tags, route mixes, and backend variance after deployment. Pair CI budgets with production RUM and alert on regression against a known baseline:

SignalSuggested alert
LCP p75Above 2.5s for 3 consecutive deploy windows
INP p75Above 200ms vs previous baseline
CLS p75Above 0.1 after release
Failed route budgetEndpoint error rate above baseline

Support Notes

  • Tooling, browser-automation behavior, and CI environments can vary across platforms, so verify the intended workflow in the environments the team actually ships and tests against.
  • Document any fallback when a browser-specific testing capability is unavailable in part of the supported matrix.

Verification

Automated Checks

  • Open the CI run for a representative pull request and confirm bundle-size and Lighthouse assertions execute on every change.
  • Verify the budgets fail the pipeline when a threshold is exceeded instead of only printing a warning.
  • Store the chosen budgets in version control so changes are explicit and reviewable.

Manual Checks

  • Review thresholds quarterly so they tighten as the app improves rather than drifting upward.
  • Review production dashboards after each release and confirm alerts fire on regressions, not only when someone notices a log line later.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Does this project have performance budgets defined? Check package.json and CI configuration for size or Lighthouse thresholds.

Fix

Auto-fix issues

Set up bundle size limits with size-limit and Lighthouse CI assertions in the CI pipeline.

Explain

Learn more

Explain performance budgets, how to choose appropriate thresholds, and how to enforce them with size-limit and Lighthouse CI.

Review

Code review

Inspect CI workflows, `package.json`, and Lighthouse or bundle-size config for explicit thresholds. Flag repos where budgets are missing, too loose to catch regressions, or unenforced on pull requests. Check whether production RUM and alerting are wired to detect regressions after release.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Bundlesizegithub.comTool
Lighthouse CIgithub.comTool
size-limitgithub.comTool

Rules that often go hand-in-hand with this one.

Split large JavaScript bundles

Use dynamic imports and route-based code splitting to break large bundles into smaller chunks that load on demand, reducing initial page load time.

JavaScript
Include accessibility testing

Automate accessibility testing with tools like axe-core, jest-axe, or Playwright's accessibility testing.

Testing
Serve images at the correct display size

Images are not significantly larger than their display dimensions—serving a 2000px image for a 400px container wastes bandwidth and hurts LCP.

Images

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385