Core Web Vitals Audit Spreadsheet: Track, Score & Prioritize Fixes

Download a Core Web Vitals audit spreadsheet to track LCP, INP, and CLS scores. Auto-prioritize fixes by impact vs effort with built-in scoring formulas.

BugViso

14 min read

A development team ships a CLS fix for their product listing page. Two sprints later, a designer adds a dynamically loaded promotional banner without explicit dimensions. The CLS score regresses from 0.04 to 0.31 — but nobody notices because there's no tracking system in place. The regression lives in production for six weeks until a quarterly Lighthouse audit finally surfaces it, after Google has already demoted the page from position 3 to position 11.

This failure pattern — fix, regress, rediscover — happens because most teams audit Core Web Vitals reactively rather than tracking them systematically. A structured Core Web Vitals audit spreadsheet transforms CWV management from ad-hoc Lighthouse runs into a scored, prioritized, and time-series tracked engineering workflow.

This guide walks through building a production-grade CWV tracking spreadsheet with auto-prioritization formulas, provides exact column schemas and scoring logic, and shows how to pipe field data from the Chrome UX Report (CrUX) API directly into your tracking system.


Why Spreadsheet-Based CWV Tracking Beats Ad-Hoc Lighthouse Runs

Running Lighthouse on individual pages produces lab data snapshots that suffer from three structural limitations:

LimitationImpact
No historical trackingYou see today's score but can't tell if it improved or regressed from last week
No cross-page visibilityA 50-page site requires 50 separate Lighthouse runs — nobody does this consistently
No prioritizationLighthouse reports every issue equally — a 0.01 CLS shift gets the same attention as a 0.45 CLS violation
Lab vs field gapLighthouse simulates a single device profile; real users on varied hardware produce different scores

A structured spreadsheet solves all four problems by establishing a single source of truth where every page's CWV scores, historical trends, responsible owners, and fix priorities live in one sortable, filterable view.


The Core Spreadsheet Architecture: 4 Tabs

Tab 1: Page Inventory & Current Scores

This is the master registry. Every page that matters for organic traffic gets a row.

ColumnData TypeSourceExample
Page URLURLSitemap / crawl/products/wireless-headphones
Page TypeEnumManualPDP, PLP, Blog, Landing
Monthly SessionsIntegerGA4 / Search Console12,450
LCP (p75, ms)IntegerCrUX API / field data2,340
INP (p75, ms)IntegerCrUX API / field data287
CLS (p75)FloatCrUX API / field data0.08
LCP StatusEnum (auto)FormulaGood / Needs Improvement / Poor
INP StatusEnum (auto)FormulaGood / Needs Improvement / Poor
CLS StatusEnum (auto)FormulaGood / Needs Improvement / Poor
Overall CWVPass/Fail (auto)FormulaPASS (all three green) or FAIL
OwnerTextManual@frontend-team
Last AuditedDateManual / script2026-09-15

Status Classification Formulas

text
// LCP Status (cell F2 contains LCP in ms)
=IF(F2<=2500, "Good", IF(F2<=4000, "Needs Improvement", "Poor"))

// INP Status (cell G2 contains INP in ms)
=IF(G2<=200, "Good", IF(G2<=500, "Needs Improvement", "Poor"))

// CLS Status (cell H2 contains CLS score)
=IF(H2<=0.1, "Good", IF(H2<=0.25, "Needs Improvement", "Poor"))

// Overall CWV Pass/Fail
=IF(AND(I2="Good", J2="Good", K2="Good"), "PASS", "FAIL")

Tab 2: Issue Registry & Prioritization Matrix

Every detected CWV issue gets its own row with a calculated priority score.

ColumnData TypeExample
Issue IDAuto-incrementCWV-042
Affected Page(s)URL(s)/products/* (pattern)
MetricEnumLCP / INP / CLS
Current ValueNumber3,200ms
Target ValueNumber<2,500ms
Root CauseTextUnoptimized hero image (2.4MB PNG)
Fix DescriptionTextConvert to AVIF, add srcset, preload
Impact Score (1-5)Integer5 (high traffic page, severe regression)
Effort Score (1-5)Integer2 (image optimization, low complexity)
Priority ScoreFormula=Impact * (6 - Effort) → 20
StatusEnumBacklog / In Progress / Deployed / Verified
SprintText2026-Q3-S4
Verification DateDate2026-09-20
Post-Fix ValueNumber1,890ms

The Priority Scoring Formula

The priority score combines impact severity with effort inversion — high-impact, low-effort fixes surface first:

text
// Priority = Impact × (6 - Effort)
// Impact 5, Effort 1 → 5 × 5 = 25 (quick wins — do first)
// Impact 5, Effort 5 → 5 × 1 = 5  (major projects — plan carefully)
// Impact 1, Effort 5 → 1 × 1 = 1  (deprioritize — low ROI)

💡 Engineering Rule of Thumb: Sort the Issue Registry by Priority Score descending. The top 5 issues will almost always deliver 80% of the CWV improvement for 20% of the engineering effort. Present this sorted list in sprint planning to justify CWV work with quantified impact.

Tab 3: Historical Trend Tracking

This tab records weekly or bi-weekly CWV snapshots for regression detection.

ColumnData Type
Snapshot DateDate
Page URLURL
LCP (p75)Integer
INP (p75)Integer
CLS (p75)Float
Delta LCPFormula: =C2-VLOOKUP(...) (vs previous snapshot)
Delta INPFormula
Delta CLSFormula
Regression FlagFormula: =IF(OR(F2>100, G2>50, H2>0.05), "⚠️ REGRESSION", "")
text
// Regression detection formula (cell I2)
// Flags if LCP worsened by >100ms, INP by >50ms, or CLS by >0.05
=IF(OR(F2>100, G2>50, H2>0.05), "⚠️ REGRESSION", "✅ Stable")

This tab is the early warning system. When a deployment introduces a font loading change, a new third-party script, or a layout modification, the next snapshot immediately flags the regression — before Google's 28-day CrUX rolling average reflects it in Search Console.

Tab 4: CrUX API Data Pipeline

Automate data collection by pulling field data from the Chrome UX Report API:

python
# crux_to_sheets.py — Pull CrUX field data into your spreadsheet
import requests
import csv
from datetime import datetime

CRUX_API_KEY = "YOUR_API_KEY"
CRUX_ENDPOINT = "https://chromeuxreport.googleapis.com/v1/records:queryRecord"

PAGES = [
    "https://example.com/",
    "https://example.com/products/wireless-headphones",
    "https://example.com/blog/best-headphones-2026",
]

def fetch_crux_data(url: str) -> dict:
    """Fetch p75 CWV field data for a single URL from CrUX API."""
    payload = {
        "url": url,
        "formFactor": "PHONE",  # Mobile field data
        "metrics": [
            "largest_contentful_paint",
            "interaction_to_next_paint",
            "cumulative_layout_shift"
        ]
    }
    resp = requests.post(
        f"{CRUX_ENDPOINT}?key={CRUX_API_KEY}",
        json=payload
    )
    if resp.status_code != 200:
        return {"url": url, "lcp": "N/A", "inp": "N/A", "cls": "N/A"}

    metrics = resp.json().get("record", {}).get("metrics", {})
    return {
        "url": url,
        "lcp": metrics.get("largest_contentful_paint", {})
                      .get("percentiles", {}).get("p75", "N/A"),
        "inp": metrics.get("interaction_to_next_paint", {})
                      .get("percentiles", {}).get("p75", "N/A"),
        "cls": metrics.get("cumulative_layout_shift", {})
                      .get("percentiles", {}).get("p75", "N/A"),
    }

# Fetch and export
rows = [fetch_crux_data(url) for url in PAGES]
filename = f"crux_snapshot_{datetime.now().strftime('%Y%m%d')}.csv"

with open(filename, "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=["url", "lcp", "inp", "cls"])
    writer.writeheader()
    writer.writerows(rows)

print(f"Exported {len(rows)} rows to {filename}")
bash
# Run weekly via cron and import the CSV into your spreadsheet
python3 crux_to_sheets.py
# Output: crux_snapshot_20260916.csv

Building the Impact-Effort Matrix Visualization

The most actionable view in the spreadsheet is a scatter plot of Impact (Y-axis) vs Effort (X-axis), divided into four quadrants:

QuadrantImpactEffortAction
Quick Wins (top-left)HighLowDo immediately — maximum ROI
Major Projects (top-right)HighHighPlan into roadmap — significant effort justified by impact
Fill-Ins (bottom-left)LowLowBatch during slack time — cheap but low value
Deprioritize (bottom-right)LowHighSkip unless mandated — poor ROI

Common Quick Wins by Metric

Diagram
LCP Quick Wins (Impact: High, Effort: Low):
├── Convert hero images from PNG/JPG to WebP/AVIF (30-60% size reduction)
├── Add width/height attributes to LCP images (eliminates CLS overlap)
├── Preload LCP image with <link rel="preload" as="image">
└── Set fetchpriority="high" on the LCP <img> element

INP Quick Wins:
├── Add loading="lazy" to below-fold images (reduces main thread work)
├── Defer non-critical third-party scripts (analytics, chat widgets)
├── Break up event handlers exceeding 50ms with requestIdleCallback
└── Replace synchronous JSON.parse on large payloads with streaming

CLS Quick Wins:
├── Add explicit width/height to all <img> and <video> elements
├── Reserve space for ad slots with min-height on container
├── Use font-display: optional or size-adjust on @font-face
└── Set contain: layout on dynamically injected content containers

Scoring Your Site: The Aggregate CWV Health Score

Transform raw per-page metrics into a single site-wide health score that executives can track quarterly:

text
// Site-Wide CWV Health Score Formula
// Weight pages by traffic (monthly sessions)

CWV_Health = Σ(page_weight × page_pass) / Σ(page_weight) × 100

Where:
  page_weight = monthly_sessions for that page
  page_pass   = 1 if all three CWV metrics are "Good", else 0

Example:
  Homepage:     15,000 sessions × 1 (PASS)  = 15,000
  Product page:  8,000 sessions × 0 (FAIL)  =      0
  Blog index:    3,000 sessions × 1 (PASS)  =  3,000
  Total weight: 26,000
  CWV_Health = (15,000 + 0 + 3,000) / 26,000 × 100 = 69.2%
text
// Spreadsheet formula for CWV Health Score
=SUMPRODUCT((L2:L100="PASS")*1, E2:E100) / SUM(E2:E100) * 100

// Where column L = "Overall CWV" (PASS/FAIL), column E = Monthly Sessions

This traffic-weighted score prevents a low-traffic 404 page with bad CLS from dragging down the overall score equally with your homepage. A site with 95% of its traffic on CWV-passing pages scores 95%, even if a handful of legacy pages fail.


Tracking Improvement Over Time: The Velocity Metric

Beyond the current health score, track the improvement velocity — how many CWV issues your team resolves per sprint:

text
// CWV Fix Velocity (per sprint)
Velocity = COUNT(issues where status changed to "Verified" in this sprint)

// Weighted Velocity (accounts for issue priority)
W_Velocity = SUM(Priority_Score of issues verified this sprint)

// Burndown: remaining issues weighted by priority
Burndown = SUM(Priority_Score of issues NOT in "Verified" status)
SprintIssues FixedWeighted VelocityRemaining BurndownHealth Score
2026-Q3-S135214861%
2026-Q3-S25787074%
2026-Q3-S34452589%
2026-Q3-S4225096%

This burndown view translates CWV work into sprint-compatible metrics that engineering managers can present alongside feature velocity.


How BugViso Automates CWV Data Collection at Scale

Building and maintaining a CWV audit spreadsheet manually works for sites with 10–20 key pages. For larger sites — e-commerce catalogs, multi-language portals, content-heavy publishers — manual Lighthouse runs and CrUX API calls don't scale.

BugViso's multi-page crawl engine automates the data collection layer:

  • Per-page CWV capture using the official web-vitals library (LCP, CLS, INP, FCP, TTFB) injected into a headless Chromium session for every crawled page — not a Lighthouse simulation, but actual browser rendering metrics.
  • Site-wide aggregation reporting total pages crawled, average and worst-page LCP, and total CLS/accessibility violations across the crawl — the exact data points that populate Tab 1 of the spreadsheet.
  • Network throttling simulation via the Advanced Speed & Performance Engine that re-loads under Slow 3G and Fast 3G profiles, revealing performance regressions hidden under fast office networks.
  • Code coverage analysis identifying unused JavaScript and CSS bytes shipped in initial bundles — the root cause data that populates the Issue Registry (Tab 2) with specific fix targets.
  • Remediation playbook that pairs each detected issue with exact copy-paste code snippets, terminal commands, and configuration changes — eliminating the "Root Cause" and "Fix Description" research that consumes most of the spreadsheet authoring time.

The audit results map directly to the spreadsheet schema: page URLs, per-page CWV scores, identified issues with severity ratings, and actionable fix instructions — exported from a single automated crawl rather than 50 manual Lighthouse runs.


Common Spreadsheet Mistakes to Avoid

Mistake 1: Tracking Lab Data Instead of Field Data

Lighthouse scores are lab measurements on a simulated Moto G Power with throttled CPU. Google's ranking algorithm uses field data from the Chrome UX Report (28-day rolling p75 from real users). Track CrUX field data in your spreadsheet, and use Lighthouse lab data only for debugging specific issues.

Mistake 2: Equal-Weighting All Pages

A 404 error page with 12 monthly visits and a 4.2s LCP should not receive the same priority as your homepage with 50,000 visits and a 2.8s LCP. Always weight pages by traffic when calculating aggregate scores and fix priorities.

Mistake 3: Not Tracking Post-Fix Verification

An issue marked "Deployed" is not "Fixed" until the field data confirms the improvement. Add a "Post-Fix Value" column and a "Verification Date" that records the CrUX p75 value 28+ days after deployment.

Mistake 4: Ignoring Cross-Metric Interactions

Fixing LCP by lazy-loading images can worsen CLS if the lazy-loaded images lack explicit dimensions. Always re-measure all three CWV metrics after deploying a fix for any single metric.


Frequently Asked Questions

How often should I update the CWV spreadsheet?

Bi-weekly matches the CrUX API's data refresh cadence and aligns with most sprint cycles. Weekly is useful during active optimization sprints. Monthly is the minimum — any less frequent and regressions go undetected for too long.

Should I track all pages or just high-traffic ones?

Track the top 20–30 pages by organic traffic plus any pages in active development. For e-commerce, include template representatives (one PDP, one PLP, one category page) rather than every individual product URL.

What's a good target CWV Health Score?

Aim for 90%+ of traffic-weighted pages passing all three CWV thresholds. Google's own data shows that sites achieving this level see measurable ranking improvements in competitive verticals. A score below 70% indicates systemic performance issues requiring architectural intervention.

Can I automate the entire spreadsheet?

Yes. Combine the CrUX API script (Tab 4) with Google Sheets API or a Python gspread integration to auto-populate scores bi-weekly. For issue detection and fix suggestions, pipe the output of an automated site-wide crawl into Tab 2. A free BugViso audit provides the per-page CWV data and remediation playbooks that populate both the Page Inventory and Issue Registry tabs.

How do I present CWV progress to non-technical stakeholders?

Use the traffic-weighted Health Score (single percentage) and the burndown chart. These translate CWV work into business-language KPIs: "We improved from 61% to 96% site health this quarter, reducing bounce rate on our highest-traffic pages by an estimated 12%."

Found this useful? Share it.

See where your site stands

Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.