Performance Metrics

Collect Core Web Vitals and resource timings from the pages your tests already visit.

The playwright-php/performance package samples browser-side paint, layout, interaction, navigation, and resource timing entries from the pages Playwright drives.

bash
composer require --dev playwright-php/performance

Collect the vitals

php
use Playwright\Performance\Monitor\PerformanceMonitor;
use Playwright\Playwright;

$browser = Playwright::chromium();
$page = $browser->newPage();

$monitor = new PerformanceMonitor($page);
$monitor->navigate('https://example.com');

$vitals = $monitor->collectCoreWebVitals();
$resources = $monitor->collectResourceMetrics();

$browser->close();

What you get

php
echo $vitals->lcp;   // Largest Contentful Paint (ms)
echo $vitals->fcp;   // First Contentful Paint (ms)
echo $vitals->cls;   // Cumulative Layout Shift
echo $vitals->inp;   // Interaction to Next Paint (ms)
echo $vitals->fid;   // First Input Delay
echo $vitals->ttfb;  // Time to First Byte (ms)

TBT (Total Blocking Time) rounds out the set. collectResourceMetrics() returns per-resource timing entries for downstream analysis: budgets, regressions, slow-asset hunts.

Where it fits

Performance checks ride along with E2E flows because the pages are already loading in a real browser. The collector can wait up to five seconds for entries to settle, so keep these checks focused.

Recommended path

Start with a small number of budgets on pages that matter: homepage, search results, checkout, dashboard, or the first authenticated page. Pick one or two metrics that map to user impact. LCP and CLS are usually easier to stabilize than a full timing dashboard.

Keep the check close to the flow:

  1. navigate or perform the user action;
  2. wait for the page state that matters;
  3. collect vitals or resources;
  4. assert broad budgets;
  5. keep artifacts when the budget fails.

Operational notes

Performance tests are sensitive to CI noise. Use thresholds that catch regressions, not exact timings. If a shared runner makes a metric unstable, move that check to a scheduled job or a controlled runner before using it as a hard gate.

The collector reports zero when an interaction metric is unavailable and uses a navigation-timing fallback when a paint entry is missing. Treat those values as test signals, not field-grade Web Vitals data.

Combine performance metrics with traces when diagnosing a failure. A metric tells you something moved; a trace helps explain what loaded, blocked, or changed.

Adoption checklist

Before adding a performance gate, decide:

  • which page or flow owns the budget;
  • which metric maps to user impact;
  • what threshold is stable in CI;
  • who acts when the budget fails;
  • whether the result is a hard gate, soft warning, or scheduled report.

Performance checks fail as a process when nobody owns the budget. A test that blocks release should have a clear owner and a clear remediation path.

What this integration is not

It is not a lab-grade benchmark and not a replacement for real-user monitoring. CI does not reproduce every device, geography, cache state, browser extension, or network condition.

Use it as a regression tripwire for controlled pages. Use production telemetry to understand real users.

Troubleshooting

  • LCP varies too much: use a broader budget, pin fixture data, and check external resources.
  • Resource count spikes: inspect third-party scripts, images, fonts, and lazy-loaded assets.
  • INP or FID is zero: the test may not include a meaningful interaction.
  • CI-only failures: compare CPU load, cache state, viewport, network access, and runner image.

FAQ

Should performance be a release gate?

Only after the signal is stable. Start as a report, then promote important budgets to gates.

Does this replace real-user monitoring?

No. It catches controlled regressions before merge. Real-user monitoring still owns field behavior.

See also