Performance details

Did the paragraph jump when the hero loaded? That's CLS. It's preventable.

## Image dimensions for CLS Every `<img>` and `<video>` needs explicit `width` and `height`. Without them, the browser reserves zero space until the image loads, then content jumps. Set _intrinsic_ dimensions as HTML attributes: ```html <img src="/hero.jpg" width="1920" height="1080" alt="Sunset over the city" /> ``` Or use `aspect-ratio` in CSS: ```css .responsive-img { width: 100%; height: auto; aspect-ratio: 16 / 9; } ``` Next.js `<Image>` requires width and height (or `fill`) and handles this automatically. ## `fetchpriority` and `loading` ```html <!-- Above the fold: load immediately --> <img src="/hero.jpg" width="1920" height="1080" alt="..." fetchpriority="high" /> <!-- Below the fold: defer until near viewport --> <img src="/screenshot.jpg" width="800" height="600" alt="..." loading="lazy" /> ``` `fetchpriority="high"`: use for the LCP element only. In Next.js: `<Image priority />`. `loading="lazy"`: defers until near viewport. **Never on the LCP image.** ## Font loading: preload, preconnect, font-display ### 1. Preconnect to the font CDN ```html <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> ``` ### 2. Preload critical fonts ```html <link rel="preload" href="/fonts/inter-regular.woff2" as="font" type="font/woff2" crossorigin /> ``` `crossorigin` is required or the browser double-fetches. Preload only fonts used above the fold. ### 3. `font-display: swap` ```css @font-face { font-family: "Inter"; src: url("/fonts/inter-regular.woff2") format("woff2"); font-display: swap; } ``` Tune fallback metrics to reduce the visible shift: ```css @font-face { font-family: "Inter Fallback"; src: local("Arial"); size-adjust: 107%; ascent-override: 90%; descent-override: 22%; } ``` ## Virtualization for large lists Past ~50 items, render only rows in or near the viewport. 10,000 items, ~30 DOM nodes. ```tsx import { useVirtualizer } from "@tanstack/react-virtual"; ``` For lists that don't justify full virtualization, `content-visibility: auto` skips layout and paint for off-screen blocks: ```css .list-row { content-visibility: auto; contain-intrinsic-size: 1px 64px; } ``` Virtualization breaks Cmd/Ctrl+F, anchor links, and print views. Plan for these if they matter. ## Common mistakes - **Removing width and height from images.** Set intrinsic dimensions. Let CSS scale. - **`loading="lazy"` on the LCP image.** Delays the most important image. - **`fetchpriority="high"` on every image.** Nothing is high priority if everything is. - **No `font-display` in `@font-face`.** Three seconds of invisible text. - **`crossorigin` missing on font preload.** Double-fetch. - **Preloading every font weight.** A megabyte before the page paints. - **Virtualizing a list of 20 items.** Complexity for no benefit.