Web Performance Budgets: A Practical Guide
Web Performance Budgets: A Practical Guide
A performance budget sets concrete limits on metrics that affect user experience — page weight, load time, and Core Web Vitals. Without budgets, performance degrades gradually as features are added. Budgets make regression visible and actionable.
Core Web Vitals Targets
Google's Core Web Vitals are the baseline metrics every site should track:
| Metric | Good | Needs Improvement | Poor |
|--------|------|-------------------|------|
| LCP (Largest Contentful Paint) | < 2.5s | 2.5s - 4.0s | > 4.0s |
| INP (Interaction to Next Paint) | < 200ms | 200ms - 500ms | > 500ms |
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1 - 0.25 | > 0.25 |
Setting Your Budget
Start with measurable targets:
{
"budgets": {
"javascript": "200KB gzipped",
"css": "50KB gzipped",
"images": "500KB total",
"fonts": "100KB total",
"lcp": "2.5s",
"inp": "200ms",
"cls": "0.1",
"ttfb": "800ms",
"total_page_weight": "1MB"
}
}
Measuring Performance
Lighthouse CI
Run Lighthouse in CI to catch regressions before they merge:
.github/workflows/lighthouse.yml
name: Lighthouse CI
on: pull_request
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci && npm run build
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v11
with:
configPath: ./lighthouserc.json
uploadArtifacts: true
// lighthouserc.json
{
"ci": {
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.9 }],
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"interactive": ["error", { "maxNumericValue": 3500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
}
}
}
}
Bundle Analysis
Track JavaScript bundle size with bundlewatch or size-limit:
// package.json
{
"size-limit": [
{
"path": ".next/static/chunks/**/*.js",
"limit": "200 KB",
"gzip": true
}
]
}
Improving LCP
LCP measures when the largest visible element finishes rendering:
font-display: swap and preload font files.Reducing CLS
Layout shift happens when elements move after initial render:
width and height attributes or aspect-ratio CSS.Improving INP
INP measures responsiveness to user interactions:
requestIdleCallback or scheduler.yield() for non-urgent work.Enforcing Budgets
Make budgets part of your CI pipeline so violations block merges:
Fail the build if bundle exceeds budget
npx size-limit --ci
Conclusion
Set performance budgets based on Core Web Vitals, measure them in CI with Lighthouse and bundle analyzers, and enforce them as hard gates on merges. Performance is a feature — without budgets, it erodes one dependency and one feature at a time.