Guides

Performance Budgets

Collect Core Web Vitals during rendering, enforce budgets, and see performance regressions correlated with the visual diff for the same page.

Performance Budgets

Frontguard collects performance metrics during the same render it uses for visual testing, checks them against budgets you set, and — the differentiator — correlates a budget violation with the visual diff for the same page. So a report doesn't just say "this page shifted"; it says "this page shifted and is over its LCP budget."

Enabling it

Add the perf-budgets plugin and define your budgets:

frontguard.config.ts
import { createPerfBudgetPlugin } from '@frontguard/cli/plugins';

export default {
  baseUrl: 'http://localhost:3000',
  plugins: [
    createPerfBudgetPlugin({
      budgets: {
        lcp: 2500,           // Largest Contentful Paint (ms)
        cls: 0.1,            // Cumulative Layout Shift
        ttfb: 600,           // Time to First Byte (ms)
        maxPageWeight: 1024, // KB
        maxRequests: 60,
      },
      failOnBudgetExceeded: false,

      // Run-over-run regression tracking (optional)
      trackRegressions: true,
      regressionThreshold: 0.2, // flag metrics >20% worse than last run
    }),
  ],
};

Budgets

BudgetUnitMeasures
lcpmsLargest Contentful Paint
clsCumulative Layout Shift
fidmsFirst Input Delay
tbtmsTotal Blocking Time (estimated)
ttfbmsTime to First Byte
maxPageWeightKBTotal transferred page weight
maxRequestscountNumber of resource requests

Set failOnBudgetExceeded: true to fail the run when any budget is breached.

Perf ↔ visual correlation

When a route both has a visual diff and breaches a perf budget, the two are joined in the report:

  • Inline — each regression/warning in the PR comment gets a ⚡ Performance — over budget: lcp 3.20s > 2.50s note right inside its details block.
  • Summary — a standalone ⚡ Performance budgets section lists every violation across the run (route, viewport, metric, actual, budget).

The same data is available in the console and HTML reports, and on RunResult.perf in the JSON output for programmatic use.

Run-over-run regressions

Static budgets answer "is this page within target?" Regression tracking answers a sharper question: "did this page get slower than it was last time?" Enable trackRegressions and Frontguard persists each run's metrics (under historyDir, default .frontguard/perf-history) and flags any metric that degraded by more than regressionThreshold since the previous run.

OptionTypeDefaultDescription
trackRegressionsbooleanfalsePersist metrics and flag run-over-run regressions
historyDirstring.frontguard/perf-historyWhere per-run metrics are stored
regressionThresholdnumber0.2Relative increase that counts as a regression (0.2 = 20% worse)

Tracked metrics are LCP, CLS, TTFB, and page weight (all "lower is better"). The first run establishes a baseline; subsequent runs report deltas like:

⚡ PERFORMANCE
    /            @ 1440px
      ttfb: 0.54s (↑ +35% since last run, was 0.40s)

In the PR comment this is correlated inline with the visual diff — a page that both shifted and got slower since the last run shows ⚡ Performance — regressed since last run: ttfb +35% (0.40s → 0.54s) right in its details block, plus a "regressions since last run" table in the summary.

Metrics are persisted in afterRun, after results are built — so a run that aborts midway (e.g. an accessibility failOnViolation) won't silently advance the baseline and hide a regression on the next run.

On this page