Largest Contentful Paint — when the largest image or text block in the viewport rendered. Good ≤ 2.5 s at p75.
INP
Interaction to Next Paint — approximately the worst interaction latency across the page's life, measured to the next painted frame. Good ≤ 200 ms. Replaced FID in March 2024.
CLS
Cumulative Layout Shift — the largest burst (session window) of unexpected layout movement. Good ≤ 0.1.
TTFB
Time to First Byte — network latency plus server processing.
FCP
First Contentful Paint — when any content first painted. Can be a skeleton.
TBT
Total Blocking Time — sum of main‑thread blocking beyond 50 ms per task, between FCP and TTI. A lab proxy for INP.
TTI
Time to Interactive — largely superseded by INP and TBT.
p75
75th percentile. Three of four page views were at least this good. The threshold CWV uses.
Long task
A main‑thread task ≥ 50 ms. Anything longer makes input feel laggy.
LoAF
Long Animation Frame — an API attributing slow frames to specific scripts. The best INP diagnostic.
// Image waste audit[...document.images].filter(i=>i.naturalWidth>0).map(i=>({src:i.currentSrc.split('/').pop().slice(0,40),intrinsic:i.naturalWidth,needed:Math.round(i.getBoundingClientRect().width*devicePixelRatio),waste:+(i.naturalWidth/(i.getBoundingClientRect().width*devicePixelRatio)).toFixed(2),})).filter(r=>r.waste>1.5).sort((a,b)=>b.waste-a.waste);// What is the LCP element?newPerformanceObserver(l=>{conste=l.getEntries().at(-1);console.log('LCP:',e.element,e.size,e.url,e.renderTime);}).observe({type:'largest-contentful-paint',buffered:true});// Every layout shift, with its sourcesnewPerformanceObserver(l=>{for(consteofl.getEntries()){if(!e.hadRecentInput)console.log('shift',e.value.toFixed(4),e.sources?.map(s=>s.node));}}).observe({type:'layout-shift',buffered:true});// Long tasks, livenewPerformanceObserver(l=>{for(consteofl.getEntries())console.log('long task',Math.round(e.duration),'ms');}).observe({type:'longtask',buffered:true});// Which scripts are causing long frames?newPerformanceObserver(l=>{for(consteofl.getEntries()){console.log('LoAF',Math.round(e.duration),'ms',e.scripts?.map(s=>`${newURL(s.sourceURL,location.href).hostname}${Math.round(s.duration)}ms`));}}).observe({type:'long-animation-frame',buffered:true});// DOM sizedocument.querySelectorAll('*').length;// Was this page prerendered?performance.getEntriesByType('navigation')[0].activationStart>0;
Version‑sensitive areas — always verify against the docs for your exact version rather than
from memory or from this course: Next.js caching defaults, PPR / Cache Components APIs, the
middleware/proxy file convention, and Speculation Rules browser support.