How to Use content-visibility: auto on Long Pages
This guide is part of CSS Containment & content-visibility, within Rendering & CSS Performance. Long pages — articles with many sections, documentation, product listings with hundreds of items, comment threads, social feeds — make the browser lay out and paint everything on load, even though users see only the first screen or two. On mid-range phones, that initial rendering can take hundreds of milliseconds, delaying LCP and blocking early interactions.
content-visibility: auto lets the browser skip rendering for sections that are offscreen. Applied to the right elements with sensible placeholder sizes, it is one of the highest-return CSS changes available: often a single rule that cuts initial rendering time by more than half. Applied carelessly, it causes scrollbar jumps, anchor links landing in the wrong place, and no gain at all for content near the fold.
Rapid Diagnosis
- Profile load with 4x CPU throttling; note the Layout and Recalculate Style durations and node counts.
- Measure page length in viewport heights; pages over 5–10 screens are good candidates.
- Identify natural section boundaries: article sections, list groups, cards, comment threads.
- Check for scripts measuring offscreen content, which would force rendering.
Root Cause Analysis
1. Rendering scales with content, not with what is visible. The browser lays out the whole document.
2. Long pages on slow CPUs. Phones spend several times longer than laptops on the same layout.
3. No natural skip mechanism. Without containment, browsers must render everything to know positions and sizes.
4. Overly fine granularity. Applying content-visibility to thousands of tiny elements adds bookkeeping overhead.
Step-by-Step Resolution
1. Choose the boundary elements
Pick elements that are large enough to matter (several hundred pixels tall) and numerous: article sections, groups of cards, comment threads. For lists, group items into chunks (for example 10–20 cards per group) rather than applying the property to each card.
2. Apply the property below the fold only
/* Skip rendering for sections after the first two. */
.article > section:nth-of-type(n + 3),
.product-grid > .group:nth-of-type(n + 2) {
content-visibility: auto;
contain-intrinsic-size: auto 900px;
}
/* trade-off: :nth-of-type thresholds are approximate; on very tall screens the
third section may already be visible, which costs a little extra work only. */
3. Provide realistic intrinsic sizes
Estimate typical section height per template. With auto, the browser uses the estimate until the section has rendered once, then remembers its real size.
4. Remove scripts that measure skipped content
Replace scroll-position calculations that read every section's geometry with IntersectionObserver, which works with skipped content without forcing it to render.
Verification
Re-profile load and compare Layout and Recalculate Style times. Scroll slowly and quickly through the page and check that content appears without blank flashes and the scrollbar does not jump noticeably. Use find-in-page for text deep in the page and follow anchor links to late sections. Confirm CLS did not increase in the lab and the field.
Worked Example: A Long-Form Publisher
A long-form publisher's feature articles averaged 9,000 words with embedded images and pull quotes, rendering about 6,500 elements. On a mid-range Android device, initial layout took 480ms and LCP p75 was 2.9s. The team wrapped each H2 section in a section element and applied content-visibility: auto; contain-intrinsic-size: auto 1400px from the third section onwards. Initial layout dropped to 110ms, LCP p75 to 2.4s, and TBT by 300ms. Anchor links from the table of contents initially landed a few hundred pixels off; adding scroll-margin-top and generating intrinsic size estimates from word count per section in the build fixed them.
Grouping Items in Long Lists
For lists of many small items (cards, rows, comments), applying content-visibility to every item works but creates per-item overhead, and each item's placeholder size must be accurate. Grouping items into chunks gives most of the benefit with fewer, larger boundaries. A server-rendered grid can emit groups of 12 or 24 items in wrapper elements; a client-rendered list can group items in the component. Groups also suit infinite scroll well: each appended page of results becomes a group that the browser can skip once it scrolls out of view.
<div class="product-grid">
<div class="group"><!-- cards 1-12 --></div>
<div class="group"><!-- cards 13-24 --></div>
</div>
<!-- trade-off: wrapper elements change grid structure; use display: contents
only if you accept that it disables the group's own box (and containment). -->
Combining with Server Rendering
Server-rendered long pages benefit most, because the full HTML arrives at once and would otherwise all be laid out on load. Emit section wrappers in the server template, so no client-side work is needed to apply the property.
Common Mistakes
- Applying it to the first screen. Adds work at the moment that matters most.
- Forgetting contain-intrinsic-size. Skipped sections collapse, the page becomes short, and the scrollbar jumps.
- Using it on tiny elements. Bookkeeping overhead outweighs the savings.
- Measuring skipped content in JavaScript. Forces rendering of everything you meant to skip.
Edge Cases
Anchor navigation. Browsers render the target section, but intrinsic size estimates for sections above it can shift the final position; auto sizes and realistic estimates minimise this.
Printing. Skipped content is rendered for printing; no special handling needed.
Images in skipped sections. Images in skipped content are not loaded until the section renders, which acts as lazy loading; keep loading="lazy" anyway for older browsers.
Hash-change navigation in SPAs. Scroll restoration may land slightly off on first visit before real sizes are remembered.
FAQ
How much faster will my page be?
It depends on how much content is offscreen. Long pages often see 50–80% reductions in initial rendering time; short pages see little or none.
Does content-visibility affect SEO?
No. The content stays in the DOM and is accessible to crawlers.
Should I use content-visibility on every list item?
Usually better to group items. Per-item use works but has more overhead and needs accurate sizes for each item.
Does it work with infinite scroll?
Yes. Apply it to each appended page or group, so groups scrolled past are skipped. For very long sessions, combine with virtualization to cap DOM size.
What happens when the user scrolls quickly?
The browser renders sections as they approach the viewport. On very fast scrolls on slow devices, a brief blank area may appear; a reasonable intrinsic size keeps the layout stable while it renders.
Does content-visibility reduce memory use?
Slightly, for rendering data structures, but the DOM is unchanged. Use virtualization if memory is the concern.
Can I use content-visibility on table rows?
Table layout depends on all rows, which limits the benefit; support for containment on table parts is also restricted. Group rows in separate tables or use a grid layout for very long tabular data.
Does content-visibility work in all browsers?
Current versions of Chromium-based browsers, Firefox and Safari support it. Older browsers ignore the property and render everything, which is the same as not using it, so it is safe as a progressive enhancement.
Should I combine it with lazy-loading images?
Yes. Keep loading="lazy" on images in skipped sections; it covers browsers without content-visibility support and makes the intent explicit.
Does it change how the page prints?
No. Browsers render skipped content when printing, so printed output includes every section.
Related
- contain-intrinsic-size to stop scrollbar jumps — getting placeholder sizes right.
- Virtualizing long lists in React — when the DOM itself must shrink.
- CSS containment & content-visibility — the topic overview.