Performance Budgets¶
This document specifies the Core Web Vitals (Lighthouse) and bundle-size budgets enforced by the .github/workflows/perf.yml CI gate.
Overview¶
Performance budgets are enforced on every PR and push to main via:
- Lighthouse CI — measures synthetic performance against Core Web Vitals thresholds (LCP, CLS, TBT).
- Bundle Size Check — gates total
.next/static/chunkssize against uncompressed and gzipped budgets.
Both gates fail the workflow on budget breach, triggering a required branch-protection block.
Core Web Vitals (Lighthouse)¶
Measured synthetically on the homepage (/) via Lighthouse CI running against a booted Next.js app.
| Metric | Budget | Baseline | Notes |
|---|---|---|---|
| First Contentful Paint (FCP) | ≤ 2500 ms | ~1800 ms | Lab metric; time to render first content |
| Largest Contentful Paint (LCP) | ≤ 3500 ms | ~2400 ms | Core Web Vital (lab); primary interaction/rendering metric |
| Cumulative Layout Shift (CLS) | ≤ 0.1 | ~0.05 | Core Web Vital (lab); visual stability |
| Total Blocking Time (TBT) | ≤ 250 ms | ~150 ms | Lab proxy for Interaction to Next Paint (INP), which is a field metric. TBT is measured in synthetic runs; INP comes from real-user monitoring. See Web Vitals guide for context. |
| Performance Score | ≥ 75 | ~82 | Lighthouse performance score |
Baseline¶
Measured on 2026-05-25 with:
- Next.js 16.2.6 (Turbopack)
- CopilotKit 1.58.0
- React 19.2.6
- 1 run on ubuntu-latest (GitHub Actions)
Raw Lighthouse output:
FCP: 1.8 s
LCP: 2.4 s
CLS: 0.05
TBT: 150 ms
Performance: 82
Updating CWV Budgets¶
If a legitimate code change increases these metrics:
-
Measure the new baseline with your change:
npm run next:build npm run next:start & npx @lhci/[email protected] autorun --config=lighthouserc.json -
Update the budget in
lighthouserc.json:{ "ci": { "assert": { "assertions": { "largest-contentful-paint": ["error", { "maxNumericValue": <new_budget_ms> }], ... } } } } -
Document the change in this file with the date, PR, and reason.
Bundle Size¶
Measured via scripts/check-bundle-size.js, which sums all .next/static/chunks/*.js files.
| Metric | Budget | Baseline | Notes |
|---|---|---|---|
| Total (uncompressed) | ≤ 21 MB | 18.17 MB | Raw JS bytes in .next/static/chunks/ |
| Total (gzipped) | ≤ 4.5 MB | 4.12 MB | Typical over-the-wire size (gzip compression) |
Baseline¶
Measured on 2026-05-25 with: - Next.js 16.2.6 (Turbopack) - CopilotKit 1.58.0 - React 19.2.6
Script output:
Total chunks (uncompressed): 18.17 MB
Budget (uncompressed): 21.00 MB
Total chunks (gzipped): 4.12 MB
Budget (gzipped): 4.50 MB
✓ All bundle-size budgets passed
Updating Bundle Budgets¶
If a change legitimately increases bundle size (e.g., adding a new dependency):
-
Build and measure:
MIDDLE_CORE_URL=http://127.0.0.1:9 npm run next:build npm run perf:bundle -
Update budgets in
scripts/check-bundle-size.js:const budgets = { totalChunksUncompressed: <new_budget_bytes>, totalChunksGzipped: <new_budget_bytes>, }; -
Document the change here with the date, PR, and reason.
CI Workflow (.github/workflows/perf.yml)¶
The performance gate runs two jobs:
1. Bundle Size Gate¶
- Builds the Next.js app
- Runs
npm run perf:bundle(fails on budget breach)
2. Lighthouse (Core Web Vitals)¶
- Builds the Next.js app
- Boots
next start(same pattern ase2e.yml) - Installs Chromium via Playwright
- Runs Lighthouse CI against
http://localhost:3000/ - Uploads report as artifact (always)
Both gates are required branch-protection gates; any failure blocks merge.
Troubleshooting¶
"Lighthouse CI failed to run"¶
- Ensure Chromium is available:
npx playwright install --with-deps chromium - Check Next.js is running:
curl -s http://localhost:3000 - Review
.github/workflows/perf.ymlfor Chrome path detection
"Bundle size exceeded"¶
- Run
npm run perf:bundlelocally to confirm - Analyze what changed:
git diff HEAD -- app/ package.json - If unavoidable, raise the budget and document in this file
"CWV budget breached"¶
- Re-run locally:
npm run next:build && npm run next:start - Profile with Chrome DevTools (
npm run next:devand F12) - Look for:
- New large dependencies (check
/api/**route handlers) - Unoptimized images or fonts
- Blocking third-party scripts
- Layout thrashing in component renders
- If unavoidable, raise the budget and document the reason here
Related¶
.github/workflows/perf.yml— CI workflow definitionlighthouserc.json— Lighthouse CI configuration (assertions, URL, headless flags)scripts/check-bundle-size.js— Bundle size check implementation.next/static/chunks/— Build output (measured by budget gate)