Skip to content
Back to the Lab

Skip Offscreen Rendering to Speed Up Long Pages

A deep technical guide to the CSS content-visibility property, covering what auto and hidden actually skip, why contain-intrinsic-size is required to avoid layout shift, the contentvisibilityautostatechange event, the underlying contain property, and real world performance numbers.

A dark Egnworks banner representing a long page where only the visible section is actually rendered.

A page with a few thousand DOM nodes below the fold still pays for nearly all of them on every render, since the browser has traditionally had no notion of content that is simply not visible yet, only content that has been explicitly removed with display: none. Every offscreen paragraph, card, and image still goes through style calculation, layout, and paint on first render and on every subsequent update, even though none of it is on the screen. content-visibility gives the browser permission to skip that work until the content is actually about to matter.

Why the Browser Rendered Everything Anyway#

Layout is not a per-element operation in isolation, changing one element’s size can shift everything below it, so a browser generally has to compute layout for a subtree in full before it can be confident nothing downstream changed. Without an explicit signal that a given subtree’s internal changes cannot affect anything outside it, the safe default is to keep computing layout, style, and paint for offscreen content exactly as if it were visible, in case something about it needs to be known for a scroll position calculation, a find on page match, or simply because the browser has no other basis for skipping it.

What content-visibility: auto Actually Skips#

content-visibility: auto tells the browser it is safe to skip an element’s descendants for layout and paint whenever that element is not near the viewport, while still keeping it laid out enough to behave correctly for scrolling, focus, and search.

.article-section {
  content-visibility: auto;
}

While a section styled this way is far from the viewport, the browser skips rendering its children entirely, style and layout only run on the section itself. As it approaches the viewport, the browser resumes normal rendering for its contents so it is fully painted by the time it is actually visible. Unlike display: none, content inside an auto section remains reachable by Ctrl+F, tab order, and accessibility tools even while its rendering is skipped, because the browser still knows the element exists and where it sits in the document.

Why a Skipped Element Still Needs a Size#

Skipping layout for a section’s children means the browser does not know how tall that section actually is, which is a problem the moment scrollbar size or scroll position depends on it. Left unset, a section switching between skipped and rendered states would jump in height and shift everything below it, exactly the kind of layout shift content-visibility is meant to help avoid elsewhere on the page.

contain-intrinsic-size fixes this by giving the browser a placeholder size to use while the section’s real content is skipped.

.article-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}

The auto keyword here means the browser remembers the section’s last rendered size and reuses it as the placeholder the next time it gets skipped, falling back to the explicit 600px the first time, before it has ever actually been measured. Choosing a reasonable estimate for that fallback value matters, since a placeholder far from the section’s real height still produces a visible jump the first time it renders.

content-visibility: hidden Is Not display: none#

hidden skips rendering unconditionally, regardless of where the element is relative to the viewport, and unlike auto it also removes the content from search, tab order, and accessibility, the same way display: none does.

.collapsed-panel {
  content-visibility: hidden;
}

The difference from display: none is what happens to the browser’s cached rendering state. Toggling display: none off forces the browser to lay out and paint the element from scratch. Toggling content-visibility: hidden off lets the browser reuse whatever it had already rendered before, which is why it fits a collapsible section or an inactive tab panel that gets shown and hidden repeatedly better than display: none does, the state is preserved rather than thrown away and recomputed every time.

Knowing When a Section Starts or Stops Being Skipped#

contentvisibilityautostatechange fires on an auto element whenever the browser starts or stops skipping its rendering, carrying an skipped boolean on the event.

section.addEventListener("contentvisibilityautostatechange", (event) => {
  if (!event.skipped) {
    renderChartInto(section);
  }
});

This is the hook for deferring genuinely expensive work, drawing into a canvas, initializing a chart library, running a layout heavy calculation, until a section is actually about to be rendered, rather than doing it the moment the section exists in the DOM regardless of whether it is anywhere near visible.

The Containment This Builds On#

content-visibility: auto works by implicitly applying layout, style, and paint containment to the element, the same containment available directly through the contain property.

contain valueWhat it isolates
layoutThe element’s internal layout cannot affect, and is not affected by, layout outside it
paintDescendants never paint outside the element’s bounds, so an offscreen contained element needs no paint work at all
styleCSS counters and quotes are scoped to the element rather than leaking past it
sizeThe element’s size is computed without regard to its children’s content

content-visibility is effectively contain plus the ability to skip the contained work entirely when the element is not relevant, rather than merely isolating it from the rest of the page.

Real Numbers From Adopting It#

Google’s own writeup on the property measured a real page’s render time dropping from 232 milliseconds to 30 milliseconds after adding content-visibility: auto to its offscreen sections, roughly a sevenfold improvement, and reported a general expectation of a fifty percent or greater reduction in rendering cost for a typical long page adopting the same pattern. The gain scales with how much of the page is actually offscreen at any given time, so a long article or a feed style layout benefits far more than a short page with little content below the fold.

What It Does Not Fix#

content-visibility reduces the cost of rendering content that is not currently relevant, it does not reduce the cost of the data fetching, JavaScript execution, or image decoding that a section might still trigger regardless of whether its layout is skipped. A section that fetches data on mount will still fetch that data even while its rendering is being skipped, unless that work is separately deferred, for example using the contentvisibilityautostatechange event to trigger it only once the section is about to become visible.

When to Reach for It#

SituationRecommendation
A long article, feed, or list with a large amount of offscreen contentApply content-visibility: auto with a reasonable contain-intrinsic-size estimate
A collapsible panel or inactive tab that toggles visibility repeatedlyUse content-visibility: hidden instead of display: none to preserve rendering state
A section containing an expensive canvas or chart that should only initialize once visiblePair content-visibility: auto with the contentvisibilityautostatechange event

Conclusion#

content-visibility gives the browser something it never had a way to express before, a scoped, revocable permission to skip layout and paint for content that is not currently relevant, backed by the same containment guarantees contain already provided on its own. auto handles the common case of a long page with plenty of offscreen content, hidden handles content that should stay unrendered but keep its state, and contain-intrinsic-size is what keeps either one from turning into a layout shift the moment real content replaces the placeholder.

References#

MDN: content-visibility

web.dev: content-visibility, the New CSS Property That Boosts Your Rendering Performance

MDN: contain

Can I Use: content-visibility