What are the key web performance metrics every frontend engineer should know?
Three Core Web Vitals: LCP (largest content paint — when main content arrives), INP (interaction-to-next-paint — how snappy clicks feel), CLS (cumulative layout shift — how much things jump). Plus TTFB (server response time), FCP (first paint), TBT (Total Blocking Time, Lighthouse-only). Track at p75 from real users. Each has Google thresholds: LCP <2.5s, INP <200ms, CLS <0.1.
Web performance metrics measure user-perceived speed and quality. Google's Core Web Vitals are the canonical set.
Core Web Vitals
LCP — Largest Contentful Paint
When the largest visible element (usually the hero image or main headline) finishes rendering. Proxy for "when does the user think the page is here?"
- Good: <2.5s at p75.
- Drivers: TTFB, render-blocking resources, image format/size, JS bundle parse.
- Fix: SSR/SSG, preload LCP image with fetchpriority=high, modern formats, smaller bundle.
INP — Interaction to Next Paint
The longest time from any user interaction (click, tap, keypress) to the next paint, across the entire session. Replaced FID in March 2024.
- Good: <200ms at p75.
- Drivers: long tasks on the main thread (JS hogging the CPU), heavy React re-renders, sync rendering of large components.
- Fix: split long tasks, useTransition for non-urgent updates, debounce inputs, web workers for CPU work.
CLS — Cumulative Layout Shift
Sum of all unexpected layout shifts during the page's lifetime. Each shift score = impact fraction × distance fraction.
- Good: <0.1.
- Drivers: images/iframes without dimensions, dynamic content injected above existing content, web fonts swapping in (FOIT/FOUT), banners pushing content down.
- Fix: width/height (or aspect-ratio) on media, reserve space for dynamic content, font-display: optional, transform-based animations.
Other useful metrics
TTFB — Time to First Byte
Time from request start to first byte of response. Largely server/CDN.
- Good: <800ms at p75.
- Drivers: server response time, CDN edge distance, redirects.
- Fix: CDN, edge SSR, faster server, eliminate redirect chains.
FCP — First Contentful Paint
When any content (text, image) appears. Earlier than LCP.
- Good: <1.8s at p75.
- Drivers: render-blocking CSS/JS, slow TTFB.
TBT — Total Blocking Time
Sum of all main-thread blocking time (tasks >50ms) between FCP and TTI. Lighthouse-only (lab metric). Proxy for INP.
- Good: <200ms in Lighthouse.
TTI — Time to Interactive
When the page is responsive to user input. Largely deprecated in favor of INP for field measurement.
Lab vs field
- Lab: synthetic test (Lighthouse, WebPageTest). Reproducible, but one device, one network. Good for catching regressions in CI.
- Field (RUM): real users. Variable conditions, true distribution. Use this for prioritization.
Google's Page Experience signals (CrUX) use field data from real Chrome users — that's what affects ranking.
Percentiles, not averages
p50 (median) hides the slow tail. p75 is the Google threshold. p95 catches the worst-affected users. Always track p75 + p95.
Segmentation
Always segment by:
- Device class (low/mid/high CPU/RAM).
- Network (4g/3g/2g via Network Information API).
- Geography (continent or country).
- Route (/checkout has a different bar than /home).
- First visit vs repeat.
Aggregated numbers across all users hide the actual issues.
Visualizing
- Filmstrip (WebPageTest): screenshots every 200ms — beats numbers for stakeholder communication.
- Waterfall: per-resource timing. Identify the long pole.
- Performance panel (Chrome DevTools): main-thread, paint, layout, scripting breakdown.
- CrUX dashboard (PageSpeed Insights): real-user p75 LCP/INP/CLS per page.
Common pitfalls
- Optimizing the average; ignoring p95.
- Treating Lighthouse score as the ground truth.
- Measuring on dev hardware (M1 Mac on fiber) and shipping to mobile India.
- Watching one metric (LCP) and ignoring the others — INP regressions are invisible until users complain.
- Confusing FID and INP — INP is the current standard.
Bonus: custom marks
Core Web Vitals measure generic page health. Add your own marks for product-critical flows: time-to-search-result, time-to-checkout-ready, etc. performance.mark + performance.measure + beacon to analytics.
Mental model
LCP = loads fast. INP = feels snappy. CLS = doesn't jump. If those three are green at p75 across your traffic, your perf is solid. Anything below is a prioritization target.
Follow-up questions
- •Why did INP replace FID?
- •What's the difference between TBT and INP?
- •How do you measure custom user-flow durations?
- •What's an acceptable LCP for an e-commerce site?
Common mistakes
- •Looking at averages instead of p75/p95.
- •Trusting Lighthouse over RUM for ranking decisions.
- •Watching only LCP and ignoring INP/CLS.
- •Confusing FID with INP.
- •Not segmenting by device/geography.
- •Optimizing for a synthetic test environment that doesn't reflect users.
Performance considerations
- •Web Vitals are the public language of perf — ranking signal, share with stakeholders, compare to competitors via CrUX. Investing here pays off in SEO, conversion, and engagement. Custom marks fill the gap for product-specific flows.
Edge cases
- •Pages with no interaction never produce INP — they're scored neutral.
- •User-initiated layout shifts (clicks) are excluded from CLS.
- •Background tabs don't contribute to web vitals — measurement pauses.
- •SPAs need to instrument route transitions; web-vitals doesn't fire on client-side navigations by default.
- •Bfcache (back/forward cache) loads aren't measured the same way — Chrome reports separate metrics.
Real-world examples
- •Google PageSpeed Insights publishes real-user CrUX data for every public URL.
- •Pinterest, Etsy, Shopify all publicly track and publish their Core Web Vitals.
- •Vercel Speed Insights, Cloudflare Web Analytics, Datadog RUM all surface these metrics.