Content-Visibility and Rendering Subtrees

The content-visibility CSS property lets the browser skip rendering work β€” style, layout, and paint β€” for subtrees that are currently offscreen, then resume it lazily as they scroll into view. Paired with contain-intrinsic-size, which reserves placeholder dimensions so skipped subtrees still contribute to scroll height, it can slash the rendering cost of long documents without virtualization. This is part of Layout and Paint Optimization, and it extends the boundaries established by CSS Containment Strategies.

The mechanism matters most for pages with many heavy, independent sections β€” feeds, documentation, dashboards β€” where the browser otherwise pays full layout and paint for content the user has not scrolled to.

Offscreen subtrees skip rendering work outside the viewport A scrollable document where sections inside the viewport are rendered while sections above and below the viewport have their layout and paint skipped via content-visibility auto. viewport β€” rendered section β€” rendering skipped section β€” rendering skipped section β€” laid out + painted section β€” laid out + painted section β€” rendering skipped section β€” rendering skipped above viewport below viewport contain-intrinsic-size reserves height for skipped sections β€” no scrollbar jump

This area covers applying content-visibility:auto to real layouts, the gotchas around in-page search and focus, and sizing placeholders so skipped content does not cause layout shift. For hands-on application see Using content-visibility for offscreen content, and for placeholder sizing see contain-intrinsic-size and scroll anchoring.

How Render-Subtree Skipping Works

content-visibility: auto implies contain: layout style paint on the element and adds one more behavior: when the element is not near the viewport, the browser skips style, layout, and paint of its descendants entirely. The subtree’s DOM still exists and remains accessible to script, but the rendering pipeline treats it as inert until it approaches the viewport, at which point the engine lays it out and paints it just in time.

/* Each article is an independent rendering boundary that the
   browser may skip while offscreen */
.article {
  content-visibility: auto;          /* skip rendering when offscreen */
  contain-intrinsic-size: auto 600px; /* placeholder height; remembers real size */
}

Because the implied containment establishes the element as its own layout and paint root, geometry changes inside a skipped subtree cannot dirty the rest of the document β€” the same isolation principle as CSS Containment Strategies, now applied conditionally based on viewport proximity.

The Sizing Problem

If a skipped subtree contributed zero height, the scrollbar would represent only the rendered sections, and scrolling toward skipped content would make the page grow and the scrollbar lurch. contain-intrinsic-size solves this by giving the box a placeholder size the browser uses for layout while the real content is skipped.

/* Before: skipped sections collapse to ~0 height β†’ scrollbar jumps */
.row { content-visibility: auto; }

/* After: reserve an estimate so scroll height stays stable */
.row {
  content-visibility: auto;
  contain-intrinsic-size: auto 120px; /* `auto` remembers last-rendered height */
}

The auto keyword is the key refinement: once a subtree has been rendered once, the browser stores its real last-rendered size and uses that instead of the estimate the next time it is skipped β€” so the placeholder converges on the true height after first paint.

Phase Condition Cost
Style + layout + paint Subtree near/in viewport Full, as normal
Skipped Subtree offscreen, content-visibility:auto ~0 (layout/paint omitted)
Placeholder layout Skipped subtree with contain-intrinsic-size Box reserved, no descendant work
Resumed render Subtree scrolls into range One-time layout + paint

Measuring the Win

Record a Performance trace on a long page before and after. The win shows up as a smaller initial Layout and Paint, because the browser only renders what is near the viewport. The application-level measurement workflow β€” including the in-page-search and focus gotchas β€” is detailed in Using content-visibility for offscreen content.

The flip side is layout stability. A bad contain-intrinsic-size estimate causes the page to resize as sections render, which registers as Cumulative Layout Shift. Keeping that stable is the focus of contain-intrinsic-size and scroll anchoring.

Validation

// Confirm skipped subtrees aren't producing layout shifts as they resume
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) console.log('CLS contribution', entry.value)
  }
}).observe({ type: 'layout-shift', buffered: true })
Metric Target How measured
Initial Layout duration Drops vs. baseline Performance panel
Initial Paint area Viewport-bounded Rendering β†’ Paint flashing
CLS ≀ 0.1 Layout Instability API
Time to first render of below-fold section One-time, on scroll Trace timeline