Skip to main content
Performance Testing·Lesson 5 of 5

Performance Budgets & Monitoring

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-analyzer

For 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 build

This 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 bundlesize

If 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 autorun

Key Takeaways

  • Performance budgets prevent gradual degradation by setting enforceable limits.
  • Use webpack-bundle-analyzer to 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-vitals library.
  • Combine lab testing (Lighthouse) with field data (RUM) for a complete performance picture.