Testing performance once is not enough. Without budgets and monitoring, performance degrades gradually as features are added. A performance budget is a set of limits on metrics that your team agrees not to exceed. Monitoring ensures you catch regressions in production.
What Is a Performance Budget?
A performance budget is a threshold for a performance metric that, when exceeded, triggers action. Budgets can apply to:
- Bundle size: Total JavaScript should not exceed 200KB gzipped.
- Load time: LCP should stay under 2.5 seconds on a 4G connection.
- Request count: No more than 50 HTTP requests on initial page load.
- Image weight: Total image payload under 500KB per page.
The key is making budgets specific, measurable, and enforceable in your build pipeline.
Setting Bundle Size Budgets
webpack-bundle-analyzer
Visualize what is inside your JavaScript bundles:
npm install -D webpack-bundle-analyzerFor Next.js, use the @next/bundle-analyzer package:
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// your config
});Run it:
ANALYZE=true npm run buildThis opens an interactive treemap showing every module and its size. You will quickly spot oversized dependencies — a common culprit is importing an entire library when you only need one function.
bundlesize
Enforce size limits in CI with the bundlesize package:
{
"bundlesize": [
{
"path": ".next/static/chunks/main-*.js",
"maxSize": "80 kB"
},
{
"path": ".next/static/chunks/pages/**/*.js",
"maxSize": "120 kB"
}
]
}npx bundlesizeIf any file exceeds its budget, the check fails. Add this to your CI pipeline to catch bundle size regressions before they merge.
Lighthouse CI Budgets
Lighthouse CI lets you set budgets on Lighthouse metrics. Create a budget.json file:
[
{
"path": "/*",
"timings": [
{ "metric": "largest-contentful-paint", "budget": 2500 },
{ "metric": "first-contentful-paint", "budget": 1800 },
{ "metric": "cumulative-layout-shift", "budget": 0.1 },
{ "metric": "total-blocking-time", "budget": 200 }
],
"resourceSizes": [
{ "resourceType": "script", "budget": 200 },
{ "resourceType": "image", "budget": 500 },
{ "resourceType": "total", "budget": 800 }
],
"resourceCounts": [
{ "resourceType": "third-party", "budget": 10 },
{ "resourceType": "total", "budget": 50 }
]
}
]Reference it in your Lighthouse CI config:
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/'],
startServerCommand: 'npm run start',
},
assert: {
budgetsFile: './budget.json',
},
},
};Real User Monitoring (RUM)
Lab tests simulate performance under controlled conditions. Real User Monitoring captures actual performance data from your users' devices and networks.
Using the web-vitals Library
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics(metric: { name: string; value: number; id: string }) {
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
page: window.location.pathname,
timestamp: Date.now(),
}),
keepalive: true,
});
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);This sends performance metrics from every real user session to your analytics endpoint. You can then aggregate the data to understand:
- What percentage of users have "Good" Core Web Vitals?
- Which pages are slowest?
- Do mobile users have significantly worse performance than desktop users?
- Did a recent deployment cause a performance regression?
Automating Budget Checks in CI
Here is a GitHub Actions workflow that checks both bundle size and Lighthouse budgets:
name: Performance Budget
on: [pull_request]
jobs:
budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Check bundle size
run: npx bundlesize
- name: Run Lighthouse CI
run: npx lhci autorunKey Takeaways
- Performance budgets prevent gradual degradation by setting enforceable limits.
- Use
webpack-bundle-analyzerto identify bloated dependencies. - Enforce bundle size limits in CI with
bundlesize. - Use Lighthouse CI budgets for timing and resource size limits.
- Implement Real User Monitoring to track performance in production with the
web-vitalslibrary. - Combine lab testing (Lighthouse) with field data (RUM) for a complete performance picture.