CSScontent-visibility & intrinsic-size

content-visibility & intrinsic-size

Rendering a page isn't free even for content the user hasn't scrolled to yet — the browser still calculates layout, style, and paint for everything in the DOM. content-visibility: auto changes that: it tells the browser to skip rendering work entirely for off-screen content, and do it lazily as the user scrolls near it. For long pages, this is one of the largest, lowest-effort performance wins available in CSS.

The rendering cost model

On a normal page, adding 1,000 off-screen list elements to a feed still costs the browser style + layout + paint time for every one of them, even though the user only sees 10. content-visibility: auto defers that cost until the element is actually about to become visible.

CSS
.feed-item {
  content-visibility: auto;
}
Without content-visibility  ->  browser renders all 1,000 items up front
With content-visibility: auto  ->  browser renders ~10 visible items, defers the rest
contain-intrinsic-size — the placeholder size

When an element's rendering is skipped, the browser doesn't know its real size (since it never laid out the content). Without a hint, it collapses to zero height — which breaks scrollbar length and causes jarring layout shifts as items pop in. contain-intrinsic-size gives the browser a placeholder size to reserve instead.

CSS
.feed-item {
  content-visibility: auto;
  contain-intrinsic-size: auto 400px; /* reserve ~400px until rendered */
}
What auto does inside contain-intrinsic-size
The auto keyword tells the browser to remember the element's *last actual rendered size* the next time it becomes visible, and reuse that as the placeholder size the next time it goes off-screen — much more accurate than a single guessed number for content with variable heights.
Measuring the improvement
  • Open DevTools Performance panel, record a full page load or a fast scroll through a long list

  • Compare total "Rendering" time (style + layout + paint) with and without content-visibility: auto

  • For long feeds/articles, expect layout and paint time on initial render to drop dramatically since most content is deferred

  • Also check Cumulative Layout Shift (CLS) in Lighthouse — a well-tuned contain-intrinsic-size should keep it near zero

CSS
/* A realistic long-article setup */
article > section {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}

/* The very first section should render immediately (above the fold) */
article > section:first-child {
  content-visibility: visible;
}
Pitfall 1: find-in-page (Ctrl/Cmd+F)
Browser find can miss unrendered text
Content skipped by content-visibility: auto is not removed from the DOM, and modern Chromium/Firefox do account for it during in-page search — but this support has evolved over browser versions, and older engines may fail to find text inside not-yet-rendered sections. Test find-in-page behavior on your actual target browsers before relying on it for critical content.
Pitfall 2: accessibility tree timing
Skipped content may not be exposed yet
Content with skipped rendering is also skipped from the accessibility tree until it becomes visible, which can affect screen reader users navigating by heading or landmark before scrolling. For content that must always be discoverable (a page's table of contents, skip links), avoid applying content-visibility: auto directly to it.
Pitfall 3: nested content-visibility

Applying content-visibility: auto to both a parent and its children is usually redundant — once the parent's rendering is skipped, its children's rendering is skipped along with it. Apply it at one sensible level (usually a repeating list item or a page section), not at every nesting level.

When to use it

Good fit

Poor fit

Long social feeds, comment threads, chat histories

Short pages where everything is above the fold anyway

Long-form articles broken into sections

Content that must be instantly searchable/accessible regardless of scroll position

Large data tables or grids rendered in full

Small, already-fast components — the tuning overhead is not worth it

Start with the biggest win
The best first candidate on most sites is a long list of repeated items — a feed, a comment section, search results. Measure before and after with the Performance panel; the size of the win scales directly with how much content is off-screen.
content-visibility vs JavaScript virtualization

Before content-visibility existed, the only way to get this benefit was JavaScript-based list virtualization (react-window, react-virtualized) — libraries that manually remove off-screen DOM nodes and re-create them on scroll. Both approaches solve the same problem; the tradeoffs differ.

Approach

Pros

Cons

content-visibility: auto

Pure CSS, no library, keeps real DOM nodes (find-in-page mostly works, simpler mental model)

Less precise control over exactly what renders; needs contain-intrinsic-size tuning

JS virtualization library

Precise control, works even for extremely large lists (tens of thousands of rows)

Extra dependency, more complex, own accessibility and scroll-restoration edge cases

They can coexist
Some teams apply content-visibility: auto to the items that a virtualization library *does* keep mounted for buffering purposes, layering the CSS-level win on top of the JS-level one for very large lists.
Interaction with loading="lazy" images

content-visibility: auto and native image lazy-loading solve related but different problems — one defers rendering work for a whole subtree, the other defers network requests for a single image. They compose cleanly.

HTML
<section class="feed-item">
  <img src="/photo.jpg" loading="lazy" alt="Description" />
  <p>Caption text</p>
</section>

CSS
.feed-item {
  content-visibility: auto;
  contain-intrinsic-size: auto 400px;
}
/* The image inside still gets its own network-level laziness from
   loading="lazy"; content-visibility additionally defers the
   rendering (layout/style/paint) cost of the whole section */
A rollout checklist

Applying content-visibility: auto across an existing page is safest as a staged process rather than a blanket find-and-replace — the tuning of contain-intrinsic-size is what determines whether it helps or hurts.

  1. Identify the single longest, most repeated content pattern on the page (feed items, table rows, article sections)

  2. Apply content-visibility: auto to that one pattern first, without touching anything else

  3. Set an initial contain-intrinsic-size guess based on the typical rendered height of one item

  4. Measure Cumulative Layout Shift in Lighthouse before and after — a bad guess shows up immediately as new layout shift

  5. Switch to contain-intrinsic-size: auto <height> so the browser remembers real sizes after first render, rather than a single fixed guess

  6. Repeat for the next-longest content pattern once the first is confirmed to help

Do not apply it everywhere at once
Rolling this out to every component simultaneously makes it hard to attribute a regression (a new layout shift, a broken find-in-page case) to the specific change that caused it. Stage it one content type at a time and measure between each step.
Browser support caveats
Not universally supported
content-visibility is supported in Chromium-based browsers and Firefox but historically lagged in Safari. Because it is purely a rendering optimization and not a visual requirement, unsupported browsers simply render everything eagerly (the old, slower behavior) rather than breaking — always verify this is actually true for the specific property/value combination you rely on before shipping it as a hard performance requirement.
Restoring visibility for print or export

Skipped rendering is a screen-only performance optimization — it should not be relied upon to control what appears when a page is printed or exported to PDF, since those contexts often force full rendering anyway. Still, it is worth being explicit for print stylesheets.

CSS
@media print {
  .feed-item {
    content-visibility: visible; /* force full rendering when printing */
  }
}
Summary
  • content-visibility: auto defers style/layout/paint work for off-screen content, a major win on long pages

  • Always pair it with contain-intrinsic-size to avoid layout shift from collapsing, zero-height placeholders

  • Prefer the auto <height> form so the browser remembers real sizes after the first render

  • Roll it out incrementally, measuring CLS at each step, rather than applying it everywhere at once

  • Be mindful of find-in-page and accessibility-tree timing for content that must always be discoverable

Next
Learn the containment primitives this feature builds on in CSS Containment (contain).