Zum Hauptinhalt springen

Critical rendering path (the LCP element paints from the document)

Generated from pattern-library/registry.json — do not edit by hand; change the registry and run bun run patterns:build.

Category: Runtime Velocity · Tier: deep · Maturity: implemented · Risk: low — rendering only; the cost is document weight on one page

What the first viewport needs paints from the server HTML and its render-blocking CSS: no hydration gate on visible content, and a late-discovered LCP graphic inlined server-side once, with a hashed CSS-mask fallback for client navigation.

When to use: Use when lab or field data shows LCP landing with hydration or the load event — the LCP element is hidden until JavaScript runs, or is an image found only after CSS and layout.

Avoid when

  • The LCP asset is large (over roughly 50 KB gz), so inlining it re-sends those bytes in every uncached document
  • Most traffic is repeat visits on fast links, where a cached file beats bytes re-sent in HTML
  • The LCP element is a photo — an with fetchpriority="high" and declared dimensions is the right tool

Alternatives

  • for the late asset — prefer when a measurement shows the earlier fetch finishing earlier — on a link saturated by module preloads it did not (v10r's logo: 450 ms earlier start, same finish)
  • an with fetchpriority="high" — prefer when the LCP element is a raster image that can be a real element rather than a CSS background or mask

Docs

Code

  • src/lib/components/primitives/logo/LogoHero.svelte — Server-only inline value, non-empty client placeholder, hashed mask fallback (GitHub · GitLab)
  • src/routes/[[locale=locale]]/(public)/_components/fadeIn.ts — Hides only nodes still below the fold, so painted content never blinks out (GitHub · GitLab)

Invariants

  • Content in the first viewport is visible without JavaScript — an entrance animation runs as CSS from first paint, never behind a hydration $effect.
  • A preload is a measurement, not a default; it ships only when the lab shows the resource finishing earlier and first paint not getting later.
  • An inlined asset ships once: a server-only value, never also in the client bundle.

Emulation notes

  • Chrome does not count an opacity
    element as LCP. Revealing the hero in a hydration $effect made v10r's mobile LCP wait for ~245 KB gz of JavaScript, and with failed JavaScript the hero never appeared.
  • {@html} hydration decides the trick: for a non-empty client value that differs from the server's, Svelte keeps the server nodes (warning hydration_html_changed, suppressed in place); an EMPTY client value discards them. Give the client a one-element placeholder, never ''.
  • Inlining on both sides ships the string twice (+42 KB gz of JavaScript in v10r's case). Branch on import.meta.env.SSR so the client build tree-shakes the markup out.
  • Measured in the lab only (Slow 4G, 4x CPU, n=5): mobile home LCP 4,276 -> 1,624 ms, reproduced 4,344 -> 1,632; desktop at parity. The document grew 8 -> 50 KB, and doc_html_kb cannot see it because it measures prerendered pages only. Confirm in field data before calling it a win.

Machine-readable record: critical-rendering-path in pattern-library/registry.json.

← Back to Pattern Library

Geht dieses Pattern noch besser? Sag uns, wie.

Feedback geben