Run curated WebPerf Snippets headlessly via Playwright. Diagnose Core Web Vitals beyond what Lighthouse exposes and gate CI on real performance budgets.
Status: v0.2. Core Web Vitals, loading audit, and structural checks. See Roadmap for what's next.
Lighthouse gives you a score. The DevTools snippets give you the diagnosis — TTFB / Resource Load Delay / Element Render Delay sub-parts, LoAF script attribution, render-blocking resources, etc. This CLI runs the same curated snippets in a headless browser so you can:
- Diagnose LCP regressions in CI without copy-pasting into DevTools.
- Gate pull requests on real performance budgets.
- Automate the snippets you already run by hand.
Playwright is a peer dependency. Install both, plus the chromium browser:
npm install --save-dev webperf-snippets playwright
npx playwright install chromiumnpx webperf-snippets <url> [options]Run the default Core Web Vitals workflow (LCP + CLS, plus LCP-Subparts if LCP > 2.5s):
npx webperf-snippets https://web.devLoading audit (TTFB, FCP, render-blocking, scripts, fonts):
npx webperf-snippets https://web.dev --workflow loadingStructural checks for CI (render-blocking, fonts, priority hints, resource hints):
npx webperf-snippets https://web.dev --workflow auditMarkdown output for PR comments:
npx webperf-snippets https://web.dev --markdownJSON output (for piping into jq or CI):
npx webperf-snippets https://web.dev --jsonSingle snippet:
npx webperf-snippets https://web.dev --snippet LCP-SubpartsSynthetic INP measurement with an interaction script:
npx webperf-snippets https://web.dev --snippet INP --interact-script interactions.jsonCI gating:
npx webperf-snippets https://web.dev --budget-lcp 2500 --budget-cls 0.1| Option | Description |
|---|---|
--workflow <name> |
Workflow to run. Default: core-web-vitals. Options: core-web-vitals, loading, audit. |
--snippet <name> |
Run a single snippet by alias or Category/Name path. |
--json |
Output JSON instead of formatted text. |
--markdown |
Output GitHub-renderable markdown (for PR comments). |
--viewport <preset> |
Viewport preset: mobile (default), tablet, desktop. |
--wait <ms> |
Post-load wait before evaluating snippets. Default: 3000. |
--interact-script <path> |
JSON file with interactions to run before evaluation (for INP). |
--budget-lcp <ms> |
Exit 1 if LCP exceeds this value. |
--budget-cls <score> |
Exit 1 if CLS exceeds this value. |
--verbose |
Show all items, including passing checks. |
--headed |
Show the browser window (debug). |
-h, --help |
Show help. |
| Alias | Snippet |
|---|---|
LCP |
CoreWebVitals/LCP |
CLS |
CoreWebVitals/CLS |
LCP-Subparts |
CoreWebVitals/LCP-Subparts |
fonts |
Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold |
render-blocking |
Loading/Find-render-blocking-resources |
resource-hints |
Loading/Resource-Hints-Validation |
preload-scripts |
Loading/Validate-Preload-Async-Defer-Scripts |
priority-hints |
Loading/Priority-Hints-Audit |
critical-css |
Loading/Critical-CSS-Detection |
ttfb |
Loading/TTFB-Sub-Parts |
script-parties |
Loading/First-And-Third-Party-Script-Info |
script-loading |
Loading/Script-Loading |
lazy-atf |
Loading/Find-Above-The-Fold-Lazy-Loaded-Images |
lazy-conflict |
Loading/Find-Images-With-Lazy-and-Fetchpriority |
eager-below-fold |
Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport |
| Code | Meaning |
|---|---|
0 |
All checks passed. |
1 |
Budget violation, or a snippet errored. |
2 |
Usage error (missing URL, unknown workflow). |
GitHub Actions, fail the PR if LCP exceeds 2.5s:
- run: |
npm install --no-save webperf-snippets playwright
npx playwright install --with-deps chromium
npx webperf-snippets https://staging.web.dev --budget-lcp 2500 --budget-cls 0.1The CLI package is published to npm via a tag-based workflow. Publishing is explicit and intentional — it only happens when a cli-v* tag is pushed.
- Bump the version in
cli/package.json. - Commit the version change.
- Tag and push:
git tag cli-v0.2.0 git push origin cli-v0.2.0
- The
publish-cliCI job runs, executes the full test suite, and publishes to npm.
An alternative is to publish automatically on every push to main that touches cli/, using a version check to skip republishes. Tag-based publishing was chosen instead because it keeps releases deliberate — a passing CI on main does not mean the package is ready to ship, and a tag communicates that intent explicitly.
Tag protection rules restrict who can push cli-v* tags. Configure them under Settings → Rules → New ruleset in the repository, targeting the cli-v* tag pattern and limiting push access to admins or maintainers. This ensures only authorized collaborators can trigger a publish.
The NPM_TOKEN secret must be set in the repository settings with publish access to the webperf-snippets npm package.
- CLS in headless is conservative: layout shifts that only happen on scroll are missed unless you script the scroll.
- First navigation only: each
webperf-snippetsinvocation runs one URL. SPAs need the post-route URL passed directly. - Synthetic INP ≠ field INP:
--interact-scriptmeasures handler latency for a single scripted event. Real INP reflects the worst interaction across all user sessions — use RUM for field data.
v0.2: Loading workflow (TTFB, FCP, render-blocking, scripts, fonts), shared page session, synthetic interactions for INP, markdown reporter for PR comments.✓ Released- v0.3: GitHub Action wrapper.
- v0.4: Auth flows (login + measure logged-in pages), CrUX field-data enrichment.
- Launches headless chromium via Playwright.
- Pre-registers
PerformanceObservers for LCP and layout-shift before navigation (Chrome doesn't expose these viagetEntriesByTypewithout a buffered observer, so the runner shims it). - Navigates, waits for the page to settle.
- Evaluates each snippet's IIFE in the page context, capturing the returned object.
- Applies the workflow's decision tree to enqueue follow-up snippets.
- Renders results (human or JSON) and exits with an appropriate code.
MIT — see LICENSE.