10LOC
Chrome APIsintermediate

content-visibility: auto to skip render cost offscreen

Published May 28, 2027

.long-page section {
  content-visibility: auto;
  contain-intrinsic-size: auto 480px;
}

What

content-visibility: auto tells the browser to skip layout, style, and paint work for an element's subtree entirely while it's off-screen, and to do that work only once the element is about to become visible — rendering cost that scales with what's on screen, not with total DOM size.

Why it matters

A long page — a huge list, a document with hundreds of sections — pays rendering cost for content nobody's looking at yet, because the browser still lays out and paints every off-screen node so it's ready the instant it scrolls into view. The usual fix is a virtualization library (react-window, react-virtual) that unmounts off-screen rows from the DOM entirely and recreates them on scroll. That works, but it's a dependency, it fights browser features that expect nodes to actually exist — Ctrl+F find-in-page and fragment-link scrolling both break, because the target text or element isn't in the DOM until something scrolls to it — and it reimplements scroll-position math the browser already has. content-visibility: auto gets the skip-the-work benefit straight from the rendering engine while leaving every DOM node in place: find-in-page and anchor links keep working, because the browser knows to briefly render an off-screen match on demand when something needs to find it.

How it works

The snippet applies the property to each section of a long page, paired with a size hint:

  • content-visibility: auto forces layout, style, and paint containment on the element and skips its contents' rendering work while it's off-screen — automatically, no JS observer required to toggle it.
  • contain-intrinsic-size: auto 480px matters in practice: without a size hint, a skipped element's box collapses toward zero height, which makes the page's scrollbar length and scroll position jump as sections come into view for the first time. The 480px is a placeholder guess reserved before the browser has ever measured the real content; the leading auto tells it to remember the actual measured size the next time this element is rendered, refining the guess from then on.

Gotchas

  • Skip the contain-intrinsic-size hint and you'll see scroll-jank as unmeasured sections snap to their real height the first time they're scrolled past — always pair the two properties.
  • This does not defer loading. Images, fonts, and scripts inside a content-visibility: auto section still fetch normally; the property only skips layout/style/paint, not network requests. Pair it with loading="lazy" on images inside the section for the full effect.
  • content-visibility: auto and content-visibility: hidden are different tools. auto is scroll-driven and automatic; hidden always skips rendering regardless of visibility and has to be toggled back manually — closer to display: none, but it preserves the subtree's layout state across the toggle instead of discarding it.
  • The containment this property forces has real layout consequences beyond visibility: a position: absolute descendant that expected to position itself relative to a farther-out ancestor can no longer escape the containing element once it has layout containment.
  • This skips rendering cost, not DOM node count — a 100,000-row table still has 100,000 real DOM nodes present; if the sheer number of nodes (not the rendering work) is the bottleneck, you still need actual virtualization.

Browser support: content-visibility shipped in Chromium in 2020 (Chrome 85). Firefox and Safari added it more recently, and the feature reached Baseline "newly available" in September 2025 once Safari's implementation matured — now usable across all three engines, though Chrome has the longest production track record with it.

Related

  • loading="lazy" — defers network fetches; composes with content-visibility: auto rather than overlapping with it.
  • Virtualization libraries — still the right tool when the bottleneck is DOM node count itself, or when row heights are too unpredictable for a useful contain-intrinsic-size guess.