--- name: performance-and-web-vitals description: Lighthouse audits and Core Web Vitals — LCP, CLS, INP. Use when optimising page load, layout shift, input delay, images, fonts or render-blocking resources. metadata: priority: 8 docs: - "https://web.dev/vitals/" - "https://developer.chrome.com/docs/lighthouse/overview/" pathPatterns: - "**/*.html" - "**/*.tsx" - "**/*.jsx" - "**/*.css" - "**/*.scss" - "next.config.*" - "vite.config.*" - "app/**" - "pages/**" promptSignals: phrases: - "lighthouse" - "performance" - "web vitals" - "lcp" - "cls" - "inp" - "layout shift" - "slow" - "page speed" - "core web vitals" retrieval: aliases: - lighthouse - web vitals - performance - LCP - CLS - INP - page speed - core web vitals intents: - run a lighthouse audit - fix layout shift - improve lcp - optimise images - fix slow page - improve lighthouse score examples: - run lighthouse on this page - my LCP is too slow - fix the layout shift on this page - optimise images for performance - improve the lighthouse score --- # Performance and Web Vitals ## Run a Lighthouse Audit ```bash # CLI audit — outputs JSON and HTML report npx lighthouse https://example.com --output html --output-path ./lighthouse-report.html # Headless, useful in CI npx lighthouse https://example.com --chrome-flags="--headless" --output json --output-path ./report.json # Audit specific categories only npx lighthouse https://example.com --only-categories=performance,accessibility,seo ``` Or open Chrome DevTools → Lighthouse tab → Analyse page load. **Target scores:** | Category | Target | |---|---| | Performance | ≥ 90 | | Accessibility | 100 | | Best Practices | ≥ 95 | | SEO | ≥ 95 | --- ## Core Web Vitals ### LCP — Largest Contentful Paint *How fast does the main content appear?* **Target: ≤ 2.5s** LCP measures when the largest visible element (hero image, heading, video poster) renders. It is the user's perception of "did the page load?" **Common causes and fixes:** | Cause | Fix | |---|---| | Unoptimised hero image | Use WebP/AVIF, correct size, `fetchpriority="high"` | | Image not preloaded | `` | | Render-blocking CSS/JS | Defer non-critical JS, inline critical CSS | | Slow server response | CDN, caching headers, edge delivery | | Web font blocking render | `font-display: swap` or `optional` | ```html ... ``` Never use `loading="lazy"` on the LCP image — it delays the most important render. --- ### CLS — Cumulative Layout Shift *Does content jump around while loading?* **Target: ≤ 0.1** CLS measures unexpected layout shifts — content moving after it has rendered. Caused by images without dimensions, late-loading ads, fonts swapping, or dynamic content injected above existing content. **Common causes and fixes:** | Cause | Fix | |---|---| | Images without width/height | Always set `width` and `height` on `` | | Web font swap | Use `font-display: optional` or preload fonts | | Dynamic content above fold | Reserve space with `min-height` on containers | | Late-loading ads or embeds | Reserve fixed dimensions for ad slots | | Animations that shift layout | Animate `transform` only, never `top/left/width/height` | ```html ... ``` ```css /* Reserve space for dynamic content */ .ad-slot { min-height: 250px; } /* Animate transform, not layout properties */ .slide-in { transform: translateY(0); transition: transform 300ms; } ``` --- ### INP — Interaction to Next Paint *How quickly does the page respond to user input?* **Target: ≤ 200ms** INP measures the delay between a user interaction (click, tap, keyboard) and the next visual update. High INP makes the UI feel sluggish or frozen. **Common causes and fixes:** | Cause | Fix | |---|---| | Heavy JS on main thread | Break into smaller tasks, use `requestIdleCallback` | | Large event handlers | Debounce/throttle scroll and resize handlers | | Synchronous DOM updates | Batch DOM writes with `requestAnimationFrame` | | Third-party scripts blocking | Load third-party scripts with `async` or `defer` | | React re-renders | Memoize with `useMemo`, `useCallback`, `React.memo` | --- ## Images Images are the single biggest performance lever on most pages. ```html ... ... ``` **Rules:** - Always set `width` and `height` — prevents CLS - Use `loading="lazy"` below the fold, never on LCP image - Serve WebP or AVIF — typically 30–50% smaller than JPEG - Size images to their display size — do not serve 2000px image for a 400px slot - Use a CDN with automatic format conversion where possible --- ## Fonts Web fonts block rendering if not handled correctly. ```html ``` ```css @font-face { font-family: 'Brand'; src: url('/fonts/brand.woff2') format('woff2'); font-display: swap; /* show fallback immediately, swap when loaded */ /* font-display: optional; — never swap, use fallback if not cached */ } ``` - `font-display: swap` — good for headings, acceptable CLS - `font-display: optional` — zero CLS, font only used if cached (best for body text) - Subset fonts to the characters actually used — reduces file size by 60–80% --- ## JavaScript ```html ``` - `defer`: executes after HTML parsed, in order — use for most scripts - `async`: executes as soon as downloaded, out of order — use for independent scripts (analytics) - Never block the main thread with synchronous `