DNA🚀 PerformanceCore Web Vitals & Performance Metrics
ðŸĶ–DinosaurPerformanceWeb VitalsMetrics

Core Web Vitals & Performance Metrics

LCP, INP, CLS aren't just acronyms — they're the language of performance that connects engineering decisions to user experience and business outcomes.

Core Web Vitals & Performance Metrics

Performance metrics exist because "it feels fast" isn't measurable, reproducible, or actionable. Core Web Vitals give us a shared vocabulary between engineers, product managers, and Google's search algorithm. Senior engineers think in metrics, not intuition.

The Three Core Web Vitals

Largest Contentful Paint (LCP) — Loading

What it measures: Time until the largest visible content element renders. Usually a hero image, <h1>, or video poster.

Thresholds: Good < 2.5s | Needs Improvement 2.5-4s | Poor > 4s

What counts as LCP element:

  • <img> elements
  • <image> inside <svg>
  • <video> poster image
  • Elements with background-image via CSS
  • Block-level text elements (<h1>, <p>, etc.)

Root causes of poor LCP:

CauseImpactFix
Slow TTFB (server response)Delays everything downstreamCDN, edge computing, caching
Render-blocking CSS/JSDelays first paintInline critical CSS, defer scripts
Slow resource loadingLCP image downloads latePreload, fetchpriority="high", modern formats
Client-side renderingLCP waits for JS executionSSR/SSG, move LCP content to HTML

Optimization playbook:

<!-- Preload the LCP image with high priority -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
 
<!-- Inline critical CSS for above-fold content -->
<style>
  .hero { min-height: 60vh; display: grid; place-items: center; }
  .hero img { width: 100%; height: auto; }
</style>
 
<!-- LCP image: no lazy loading, explicit dimensions, high priority -->
<img src="/hero.webp" alt="Hero" width="1200" height="600"
     fetchpriority="high" decoding="async">

Interaction to Next Paint (INP) — Responsiveness

What it measures: The worst interaction latency during the page visit. Replaced FID (which only measured the first interaction).

Thresholds: Good < 200ms | Needs Improvement 200-500ms | Poor > 500ms

The three phases of an interaction:

User clicks button
    │
    ├── Input Delay (waiting for main thread)    ──┐
    ├── Processing Time (event handler execution)  ├── INP measures all three
    └── Presentation Delay (rendering the update)  ──┘

Root causes of poor INP:

CausePhaseFix
Long tasks blocking main threadInput delayBreak up work, use Web Workers
Heavy event handlersProcessingOptimize logic, defer non-critical work
Expensive re-rendersPresentationMemoization, virtualization, transitions
Large DOM sizePresentationSimplify DOM, use virtualization

Optimization patterns:

// ❌ Heavy handler blocks next paint
button.addEventListener('click', () => {
  const result = processThousandItems(data); // 300ms
  updateUI(result);
});
 
// ✅ Yield to browser between processing and painting
button.addEventListener('click', async () => {
  showSpinner();
  await scheduler.yield(); // Let browser paint the spinner
 
  const result = processThousandItems(data);
  updateUI(result);
});
 
// ✅ React: use transitions for non-urgent updates
const [isPending, startTransition] = useTransition();
 
function handleFilter(query) {
  setInputValue(query);              // Urgent: update input
  startTransition(() => {
    setFilteredResults(filter(query)); // Non-urgent: can be interrupted
  });
}

Cumulative Layout Shift (CLS) — Visual Stability

What it measures: Sum of all unexpected layout shifts during the page's lifetime. A layout shift happens when a visible element changes position between frames without user interaction.

Thresholds: Good < 0.1 | Needs Improvement 0.1-0.25 | Poor > 0.25

Root causes:

CauseFix
Images without dimensionsAlways set width and height
Ads/embeds without reserved spaceUse min-height placeholder
Dynamic content inserted above viewportInsert below or use content-visibility
Web fonts causing text reflowfont-display: optional or font-display: swap with size-adjust
CSS animations using layout propertiesUse transform instead
<!-- Always set dimensions to prevent shift -->
<img src="photo.jpg" width="800" height="600" alt="Photo">
 
<!-- Reserve space for dynamic content -->
<div style="min-height: 250px; contain: layout;">
  <!-- Ad or async content loads here -->
</div>
/* Prevent font-swap layout shift */
@font-face {
  font-family: 'Custom';
  src: url('/custom.woff2') format('woff2');
  font-display: optional;
  size-adjust: 105%;
  ascent-override: 95%;
}

Supporting Metrics

MetricWhat it MeasuresWhy it Matters
TTFB (Time to First Byte)Server response timeDirectly delays every other metric
FCP (First Contentful Paint)First text/image visibleUser perceives page is loading
TBT (Total Blocking Time)Sum of long task time over 50msLab proxy for INP
SI (Speed Index)How quickly content is visually populatedOverall perceived speed
TTI (Time to Interactive)When page is fully interactiveDeprecated but still referenced

Measuring: Lab vs Field

Lab (Synthetic)Field (RUM)
ToolsLighthouse, WebPageTest, DevToolsweb-vitals library, CrUX, Analytics
WhenDevelopment, CI/CDProduction, real users
ConditionsControlled (simulated device/network)Real (diverse devices, networks)
INPCan't measure (TBT used as proxy)Full INP measurement
PercentileSingle runp75 is the target (75th percentile)
import { onLCP, onINP, onCLS } from 'web-vitals';
 
function sendToAnalytics({ name, value, id, rating }) {
  analytics.track('web_vital', {
    metric: name,
    value: Math.round(value),
    rating,
    id,
    url: window.location.href,
    connection: navigator.connection?.effectiveType,
  });
}
 
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);

Performance Budgets

Set limits, not aspirations:

// Lighthouse CI config
{
  "assertions": {
    "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
    "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
    "total-blocking-time": ["error", { "maxNumericValue": 300 }],
    "resource-summary:script:size": ["error", { "maxNumericValue": 200000 }]
  }
}

Budget violations break the build. Not "warn" — break.

Interview Signal

Senior candidates demonstrate:

  1. Metric precision — Knowing what each metric measures, the three INP phases, what elements qualify for LCP
  2. Root cause mapping — Connecting poor scores to specific technical causes
  3. Fix prioritization — Addressing the highest-impact issues first (TTFB before image optimization)
  4. Measurement sophistication — Lab vs field, p75 target, RUM vs synthetic
  5. Business connection — Linking metrics to conversion rates, SEO ranking, and user retention