Core web vitals
/SKILLOptimize Core Web Vitals (LCP, INP, CLS) for better page experience and search ranking.
--- name: core-web-vitals description: Optimize Core Web Vitals (LCP, INP, CLS) for a better page experience and higher search rankings. Use when asked to "improve Core Web Vitals," "fix LCP," "reduce CLS," "optimize INP," "optimize page experience," or "fix layout shifts." license: MIT metadata: author: web-quality-skills version: "1.0" --- # Core Web Vitals optimization Targeted optimization for the three Core Web Vitals metrics that affect Google Search rankings and user experience. ## The three metrics | Metric | Measures | Good | Needs work | Poor | |--------|----------|------|------------|------| | LCP | Loading | ≤ 2.5s | 2.5s : 4s | > 4s | | INP | Interactivity | ≤ 200ms | 200ms : 500ms | > 500ms | | CLS | Visual Stability | ≤ 0.1 | 0.1 : 0.25 | > 0.25 | Google uses the 75th percentile as its benchmark:75% of page visits must meet the “Good” thresholds. --- ## LCP: Largest Contentful Paint LCP measures when the largest visible content element renders. Usually this is: - Hero image or video - Large text block - Background image - <svg> element ### Common LCP issues 1. Slow server response (TTFB > 800ms) `` Fix: CDN, caching, optimized backend, edge rendering ` **2. Render-blocking resources** `html <!-- ❌ Blocks rendering --> <link rel="stylesheet" href="/all-styles.css"> <!-- ✅ Critical CSS inlined, rest deferred --> <style>/* Critical above-fold CSS */</style> <link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> ` **3. Slow resource load times** `html <!-- ❌ No hints, discovered late --> <img src="/hero.jpg" alt="Hero"> <!-- ✅ Preloaded with high priority --> <link rel="preload" href="/hero.webp" as="image" fetchpriority="high"> <img src="/hero.webp" alt="Hero" fetchpriority="high"> ` **4. Client-side rendering delays** `javascript // ❌ Content loads after JavaScript useEffect(() => { fetch('/api/hero-text').then(r => r.json()).then(setHeroText); }, []); // ✅ Server-side or static rendering // Use SSR, SSG, or streaming to send HTML with content export async function getServerSideProps() { const heroText = await fetchHeroText(); return { props: { heroText } }; } ` **5. Make navigations instant with the Speculation Rules API** For most sites, the LCP a user actually experiences is dominated by *the next page they navigate to*, not the one they landed on. Telling the browser to prerender likely-next pages on hover collapses that LCP to ~0ms. `html <script type="speculationrules"> { "prerender": [{ "where": { "href_matches": "/*" }, "eagerness": "moderate" }] } </script> ` eagerness settings (cheapest → most aggressive): conservative (start on pointerdown), moderate (start after ~200ms hover), eager (start as soon as the link is in the viewport), immediate (start on page load). Start with moderate : it captures most navigations without prerendering pages users never visit. Caveats: - **Bandwidth/CPU cost.** Each prerender is roughly a full page load. Scope where carefully (href_matches patterns, exclude logout/checkout) and avoid immediate outside small sites. - **Side effects fire early.** Analytics, ads, and any code that runs on load will fire when the prerender starts, not when the user navigates. Gate side effects on the [ prerenderingchange event](https://developer.chrome.com/docs/web-platform/prerender-pages#detect_when_a_page_is_prerendered_or_used_for_a_full_navigation) or document.prerendering . - **Chromium-only.** Safari and Firefox ignore the script : it's a progressive enhancement, never a regression. ### LCP optimization checklist `markdown - [ ] TTFB < 800ms (use CDN, edge caching) - [ ] LCP image preloaded with fetchpriority="high" - [ ] LCP image optimized (WebP/AVIF, correct size) - [ ] Critical CSS inlined (< 14KB) - [ ] No render-blocking JavaScript in <head> - [ ] Fonts don't block text rendering (font-display: swap) - [ ] LCP element in initial HTML (not JS-rendered) - [ ] Speculation Rules added for likely-next navigations (moderate eagerness) ` ### LCP element identification ``javascript // Find your LCP element new PerformanceObserver((list) => {