๐Ÿณ IntuitiveFE
Login
โ† All concepts

Core Web Vitals & CRP

โฑ๏ธ ~3-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

5-Year-Old Metaphor

โ€” The physical, real-world picture. No jargon.

๐Ÿฉบ Vital signs for a webpage โ€” each metric answers a specific user experience question.

The five questions

TTFB

Is the server fast?

User thinks: "How long before any HTML arrives?"

FCP

Is anything happening?

User thinks: "When does the blank screen go away?"

LCP

Is the main content here?

User thinks: "When can I read/see the page?"

CLS

Is the page stable?

User thinks: "Did content just jump when I tried to click?"

INP

Is the page responsive?

User thinks: "Did my click/tap do anything?"

Critical Rendering Path (CRP)

Every web vital is shaped by the Critical Rendering Path โ€” the sequence of steps the browser must complete before pixels appear on screen.

DNS lookup~20-120msResolve domain to IP
TCP + TLS handshake~50-200msEstablish secure connection
HTTP request/TTFB~100-500msServer processes, sends first byte
HTML parse โ†’ DOM~10-100msBrowser builds DOM tree
CSS fetch + parse โ†’ CSSOM~20-500msRender-blocking โ€” can delay FCP
JS fetch + execute~50-2000msParser-blocking if synchronous
Layout โ†’ Paint โ†’ Composite~5-50msFCP / LCP fire here

Field data vs lab data

Lab data (Lighthouse)

  • โ€ข Runs in a controlled environment
  • โ€ข Throttled CPU + network
  • โ€ข Reproducible โ€” good for debugging
  • โ€ข Doesn't reflect real users
  • โ€ข CLS/INP often inaccurate in lab

Field data (CrUX)

  • โ€ข Real Chrome users (28-day window)
  • โ€ข Accounts for all devices/networks
  • โ€ข What Google uses for ranking
  • โ€ข 28-day lag โ€” slow feedback loop
  • โ€ข web-vitals.js measures in-page
๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Core Web Vital

LCP

Largest Contentful Paint

Time from navigation to largest image or text block rendered in the viewport

45

Poor

demo score

Good < 2.5sNeeds work 2.5โ€“4.0sPoor > 4.0s

Page load timeline โ€” when LCP fires

TTFB
FCP
LCP
CLS
INP

LCP: Largest element visible (~2200ms into load)

Top causes of poor LCP

โœ•Hero image not preloaded (discovered late in CSS)

โœ•No CDN for images โ€” slow origin server

โœ•Client-side rendered content (empty HTML shell)

Top fixes

โœ“<link rel='preload' as='image'> for hero images

โœ“Use next/image with priority prop for above-fold images

โœ“Server-render the LCP element (not client-side)

Gotcha: The LCP element can change as the page loads โ€” React might update the DOM, making a larger element the new LCP candidate. Chrome DevTools Performance panel shows all LCP candidates.
Insight: Images without explicit width/height cause CLS. But they also hurt LCP because the browser can't compute layout until the image loads, delaying paint. Always set width and height on img elements.
Explored:๐Ÿ“ก๐ŸŽจ๐Ÿ–ผ๏ธ๐Ÿ“โšก
๐ŸŽฏ

Challenge

Explore all 5 Core Web Vitals. For each one, answer: what user experience does it measure, and what's the single most impactful fix?

Try it
๐ŸŽฏ

Why Should I Care?

โ€” The exact interview question + the bug it kills.

Interview questions

Q: How does LCP differ from FCP?

FCP fires when ANY content is painted โ€” including spinners, skeletons, or placeholder text. LCP fires when the largest text block or image in the viewport is painted. A page with a fast-loading spinner but slow hero image can have FCP at 0.8s (good) and LCP at 4.5s (poor). LCP is a better proxy for when the page is "actually useful" to the user.

Q: What is INP and why did it replace FID?

First Input Delay (FID) measured only the FIRST interaction on the page, and only the delay before the browser could start handling it (not the full processing + paint time). INP measures ALL interactions throughout the page lifetime (clicks, taps, keyboard events) and reports the worst one. An e-commerce product page might pass FID but fail INP because the "Add to Cart" click on the third interaction is sluggish.

Bug: Images without width/height cause CLS

html
1<!-- โœ— No dimensions โ€” browser allocates 0px until image loads -->
2<img src="/hero.jpg" alt="Hero" />
3<!-- When image loads, layout shifts โ†’ CLS penalty -->
4ย 
5<!-- โœ“ Always set width + height -->
6<img src="/hero.jpg" alt="Hero" width="1200" height="600" />
7<!-- Browser reserves space โ†’ no layout shift -->
8ย 
9<!-- โœ“ Next.js: use next/image (sets dimensions + lazy-loads) -->
10<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />
๐Ÿ”ฌ

The Deep Dive

โ€” Spec refs, engine internals, the minutiae.

Measuring with web-vitals.js

js
1import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
2ย 
3function sendToAnalytics(metric) {
4 const { name, value, rating, id } = metric;
5 // rating: 'good' | 'needs-improvement' | 'poor'
6 navigator.sendBeacon('/analytics', JSON.stringify({
7 metric: name, value, rating, url: location.href,
8 }));
9}
10ย 
11onLCP(sendToAnalytics);
12onINP(sendToAnalytics);
13onCLS(sendToAnalytics);
14onFCP(sendToAnalytics);
15onTTFB(sendToAnalytics);

PerformanceObserver โ€” measuring Long Tasks for INP

js
1// Long Tasks API โ€” tasks > 50ms block INP
2const observer = new PerformanceObserver((list) => {
3 list.getEntries().forEach((entry) => {
4 console.log('Long Task:', entry.duration.toFixed(0) + 'ms');
5 // attribution: which script/function caused it
6 entry.attribution.forEach((attr) => {
7 console.log(' โ†’', attr.name, attr.containerSrc);
8 });
9 });
10});
11observer.observe({ type: 'longtask', buffered: true });
12ย 
13// Also: Event Timing API (measures interaction latency directly)
14const evObserver = new PerformanceObserver((list) => {
15 list.getEntries().forEach((entry) => {
16 if (entry.duration > 200) { // INP threshold
17 console.log('Slow interaction:', entry.name, entry.duration);
18 }
19 });
20});
21evObserver.observe({ type: 'event', buffered: true, durationThreshold: 16 });

Core Web Vitals as a Google ranking signal

Since June 2021, Google incorporates CWV into the "page experience" ranking signal. All three Core Web Vitals (LCP, INP since 2024, CLS) must be in the "Good" range for 75% of page loads (75th percentile of field data) to get the maximum ranking benefit. Lab scores (Lighthouse) do NOT affect rankings โ€” only CrUX field data matters.

๐ŸŽค

Interview Questions

โ€” Real questions from real interviews โ€” with answers.

LCP measures when the largest image or text block in the viewport is painted โ€” poor scores are usually caused by unpreloaded hero images or client-side rendered content.

CLS measures cumulative visual instability. Fix images by setting explicit dimensions; fix fonts with font-display: swap or preloading.

FID measured only the first interaction and only the input delay, not the full processing + paint time. INP measures all interactions and picks the worst.

The CRP is the sequence of steps โ€” DNS, TCP, HTML parse, CSSOM build, JS execution โ€” that must complete before the first pixel is painted.

Lab data is synthetic (throttled, reproducible); field data is real Chrome users over 28 days. Only field data from CrUX affects Google rankings.

Identify long tasks blocking the main thread with PerformanceObserver / Chrome DevTools, then break them with scheduler.yield(), memoization, or Web Workers.

๐ŸŽฎ

Memory Game

โ€” Quick quiz โ€” lock the concept in long-term memory.
1/4

What is a Long Task as defined by the browser?