How to Measure Core Web Vitals in GitHub Actions
Lighthouse reports lab values for LCP, CLS, and Total Blocking Time, which you can read from its JSON to track Core Web Vitals in CI.
Run Lighthouse against a built page, output JSON, and extract the metric audits with jq. Lab numbers are stable enough to trend even though they are not field data.
Steps
- Build and serve the site, then run Lighthouse with
--output=json. - Read
audits["largest-contentful-paint"].numericValueand friends withjq. - Compare each metric against a target and fail when it regresses.
Workflow
.github/workflows/ci.yml
jobs:
vitals:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci && npm run build && npx serve -l 8080 dist &
- run: npx lighthouse http://localhost:8080 --quiet --chrome-flags="--headless=new --no-sandbox" --output=json --output-path=lh.json
- run: |
jq '{lcp: .audits["largest-contentful-paint"].numericValue,
cls: .audits["cumulative-layout-shift"].numericValue,
tbt: .audits["total-blocking-time"].numericValue}' lh.jsonGotchas
- Lab Web Vitals differ from field data; treat them as a regression signal, not the real user value.
- CLS and TBT are proxies for the field INP and layout-shift values, not identical metrics.
Related guides
How to Enforce a Lighthouse Performance Budget in GitHub ActionsFail a GitHub Actions run when a page blows its performance budget by feeding a budget.json to Lighthouse CI…
How to Set Lighthouse Category Thresholds in GitHub ActionsBlock a GitHub Actions merge when a Lighthouse category score drops below a threshold using assertions on cat…