PerformanceObservation
PerformanceObservation holds performance and Core Web Vitals data from two sources: field data from the CrUX (Chrome User Experience Report) connector, and lab data from Lighthouse when a rendered crawl is triggered. Comparing the two is a first-class finding type.
TypeScript interface
Section titled “TypeScript interface”interface PerformanceObservation { url: string; projectId: string; observedAt: string;
// CrUX field data (from Chrome users in the real world) cruxLcp75thMs: number | null; // 75th percentile LCP in ms cruxInp75thMs: number | null; // 75th percentile INP in ms (replaced FID) cruxCls75th: number | null; // 75th percentile CLS score (unitless) cruxFcp75thMs: number | null; // 75th percentile FCP in ms cruxTtfb75thMs: number | null; // 75th percentile TTFB in ms cruxLcpRating: 'good' | 'needs_improvement' | 'poor' | null; cruxInpRating: 'good' | 'needs_improvement' | 'poor' | null; cruxClsRating: 'good' | 'needs_improvement' | 'poor' | null; cruxDeviceType: 'phone' | 'desktop' | 'tablet'; cruxObservationDate: string | null; // ISO date of CrUX data cruxDataLevel: 'url' | 'origin'; // URL-level or origin-level fallback
// Lighthouse lab data (from controlled test environment) lighthouseLcpMs: number | null; lighthouseInpMs: number | null; lighthouseCls: number | null; lighthouseFcpMs: number | null; lighthouseTtfbMs: number | null; lighthouseScore: number | null; // overall performance score 0-100 lighthouseRunAt: string | null;
// Field vs lab discrepancy fieldLabLcpDeltaMs: number | null; // CrUX - Lighthouse (positive = field worse) fieldLabDiscrepancyFlag: boolean; // field significantly worse than lab}Thresholds
Section titled “Thresholds”| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP | ≤ 2,500ms | 2,500–4,000ms | > 4,000ms |
| INP | ≤ 200ms | 200–500ms | > 500ms |
| CLS | ≤ 0.1 | 0.1–0.25 | > 0.25 |
These are Google’s thresholds. VisibilityIQ uses these exactly for rating assignments.
Field vs lab discrepancy
Section titled “Field vs lab discrepancy”A fieldLabDiscrepancyFlag = true is set when CrUX LCP exceeds Lighthouse LCP by more than 1,000ms (1 second). This is a meaningful signal because:
- Lab tests run under controlled conditions with no third-party scripts, no browser extensions, no network contention
- If real users experience significantly worse performance than the lab predicts, something is degrading field performance that lab tests don’t capture (third-party scripts, A/B testing libraries, ad networks, personalization layers, CDN configuration issues)
This generates a High severity finding with the delta as evidence.
CrUX data level
Section titled “CrUX data level”CrUX provides data at two granularities: URL-level (this specific page) and origin-level (the whole domain). URL-level is more accurate but requires sufficient traffic on that specific URL (CrUX suppresses data for low-traffic URLs). When URL-level data is unavailable, origin-level data is used as a fallback, and cruxDataLevel = 'origin' is set. This is noted in the finding evidence.