Articles

CSS content-visibility: Rendering Performance Explained

content-visibility lets the browser skip layout, style, and paint for off-screen content, cutting rendering cost on long pages without JavaScript.

Takina Takina · · 4 min read
Close-up of CSS source code on a screen

content-visibility is a CSS property that lets the browser skip rendering work — layout, style calculation, and paint — for elements that aren’t currently visible on screen. Unlike display: none, which removes an element from rendering entirely (and forces the browser to redo all that work the moment it’s shown again), content-visibility: auto keeps an element’s box in the layout while deferring the expensive internal work until it’s actually needed, typically when it scrolls into view.

The problem it solves

A browser’s critical rendering path — style, layout, paint, composite — runs for every element in the DOM, whether or not that element is on screen. A long page with thousands of DOM nodes (a product listing, a long article archive, a chat log) pays the full layout and paint cost for content the user hasn’t scrolled to yet. That cost matters most on the initial render and on any update that touches a large subtree, since the browser has to lay out and paint the whole thing before showing anything.

content-visibility tells the browser: don’t bother computing layout and paint for this element’s contents until it’s near the viewport. The browser still needs to know roughly how tall the element is so the page doesn’t jump around as content is skipped and later rendered — that’s what the companion property contain-intrinsic-size is for.

The three values

.section {
  content-visibility: visible; /* default: normal rendering */
}

.offscreen-section {
  content-visibility: auto; /* skip rendering work when off-screen */
  contain-intrinsic-size: auto 500px;
}

.collapsed-section {
  content-visibility: hidden; /* like display: none, but state is preserved */
}
  • visible — the default. No behavior change.
  • auto — the browser skips layout and paint for the element’s contents when it’s off-screen, and does the work when it scrolls near the viewport. This is the value most sites reach for.
  • hidden — content is unconditionally skipped, similar to display: none, but the element’s rendering state is cached rather than discarded. This is useful for something like a collapsed accordion panel or an inactive tab, where you want to show it again cheaply without redoing all the layout work from scratch.

Why contain-intrinsic-size matters

Without an intrinsic size hint, an element with content-visibility: auto collapses to zero height while off-screen (since the browser hasn’t computed its actual layout yet), which throws off scrollbar sizing and can cause visible layout shift as sections come into view. contain-intrinsic-size gives the browser a placeholder size to reserve in the meantime:

.card {
  content-visibility: auto;
  contain-intrinsic-size: auto 300px;
}

The auto keyword before the size tells the browser to remember the element’s last rendered size once it has been measured, rather than always falling back to the static 300px placeholder. This keeps scroll position and layout stable across repeated show/hide cycles, which matters for the layout-shift component of Core Web Vitals.

Relationship to CSS containment

content-visibility builds on the CSS containment model — the same idea behind the contain property, which tells the browser that an element’s internal layout, paint, or size won’t affect anything outside its box. Setting content-visibility: auto implicitly applies contain: layout style paint to the element, which is what makes it safe for the browser to skip work: because the element is contained, changes inside it can’t leak out and force a recalculation of the rest of the page.

Where it helps most

content-visibility pays off on pages with long, repetitive, off-screen-heavy structure:

  • Long article or documentation pages with many sections
  • Infinite-feed or paginated-list UIs with hundreds of list items in the DOM
  • Dashboards with many collapsible or tabbed panels

Because content-visibility is purely additive, browsers that don’t recognize the property simply ignore it and render everything normally — there’s no fallback branch to write, and no risk of content silently disappearing for a visitor on an older browser. That makes it a low-risk optimization to reach for on any page with the kind of structure above; the worst case for an unsupported browser is that it renders exactly as it would have without the property at all.

It’s less useful on short pages, or pages where nearly all content is above the fold anyway — there’s nothing to defer. It’s also not a substitute for lazy-loading images or code, which address network cost rather than rendering cost; the two techniques are complementary, not competing.

What it doesn’t do

content-visibility doesn’t remove elements from the accessibility tree the way display: none does for visible/hidden states differently — browsers generally still expose auto content to assistive technology and to in-page find (Ctrl+F), since the content technically exists, it’s just not rendered yet. It also doesn’t affect network requests: images inside a content-visibility: auto element may still load early depending on the browser’s own scheduling heuristics, so pairing it with loading="lazy" on images is still worthwhile for pages with a lot of off-screen media.

The takeaway

content-visibility: auto lets the browser skip layout and paint for off-screen sections of a page and pick the work back up once they scroll into view, cutting rendering cost on long or list-heavy pages without any JavaScript. Pair it with contain-intrinsic-size to avoid layout shift as sections appear, and treat it as a complement to image lazy-loading rather than a replacement for it — one defers rendering work, the other defers network work.

Takina Takina · · 5 min read

CSS will-change Explained: Compositing and Performance

The CSS will-change property hints the browser to prepare an element for an upcoming change, moving it to its own compositor layer. When to use it and when not to.

#CSS #Web Development #Performance
Takina Takina · · 4 min read

CSS Scroll-Driven Animations Explained

CSS scroll-driven animations tie keyframes to scroll position instead of a clock, running smoothly off the main thread. Here's how the timeline model works.

#CSS #Web Development #Frontend