CSS contain and content-visibility: Performance and Rendering Control

CSS containment lets you tell the browser that parts of a page are independent, while content-visibility lets the browser skip work for content that is not currently visible. Used well, these features can reduce layout, paint, and style recalculation costs on large pages.

Quick answer: Use contain when you want to isolate an element’s layout, paint, style, or size effects from the rest of the page. Use content-visibility: auto when you want the browser to skip rendering offscreen content until it is needed.

Difficulty: Intermediate

You'll understand this better if you know: basic CSS layout behavior, how the browser renders pages, and the difference between block layout, paint, and reflow.

1. What Is contain and content-visibility?

contain is a CSS property that limits how much an element can affect the rest of the page. content-visibility is a related property that can skip rendering work for an element, especially when it is offscreen.

These properties are especially useful for large lists, dashboards, long documents, expandable panels, and card grids where only part of the content is visible at once.

2. Why contain and content-visibility Matter

Modern pages often contain many nested elements, images, widgets, and text blocks. Without containment, a small change in one area can cause the browser to do more layout and paint work than necessary.

Containment helps the browser make better performance decisions:

content-visibility: auto is especially valuable for content below the fold because the browser can skip rendering it until it approaches the viewport. That can significantly improve initial load time on content-heavy pages.

3. Basic Syntax or Core Idea

The core idea is simple: use contain to isolate an element, and use content-visibility to let the browser defer rendering.

Containment syntax

Here is the basic form of contain:

.card {
  contain: layout paint;
}

This tells the browser that the element’s layout and painting effects should not leak outside the element.

Content visibility syntax

Here is the basic form of content-visibility:

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

The contain-intrinsic-size value gives the browser a placeholder size before the content is fully rendered, which helps prevent layout jumps.

4. Step-by-Step Examples

Example 1: Isolating a card component

Suppose you have a card with an image, text, and a button. If internal changes should not affect the rest of the page, containment can help.

.product-card {
  contain: layout paint;
  border: 1px solid #ccc;
  padding: 1rem;
}

This is useful when a card contains animations, badges, or internal layout changes that should stay local.

Example 2: Skipping offscreen sections

For long content sections, content-visibility: auto can avoid rendering work until the user scrolls near the section.

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

The browser may skip rendering the section until it is close to the viewport, then paint it when needed.

Example 3: Keeping expensive widgets isolated

A chart or editor inside a dashboard can be expensive to paint and relayout. Containment helps prevent changes inside the widget from affecting surrounding layout.

.dashboard-widget {
  contain: content;
}

contain: content is a shorthand that combines several containment types and is often a practical default for self-contained UI blocks.

Example 4: Reserving space for lazy-rendered content

When the browser skips rendering offscreen content, you should still reserve reasonable space to reduce layout shifts.

.lazy-panel {
  content-visibility: auto;
  contain-intrinsic-size: 400px 600px;
}

This gives the browser a fallback size so the page does not collapse while the element is skipped.

5. Practical Use Cases

These features are most useful when the page contains many large regions, not just a few small elements.

6. Common Mistakes

Mistake 1: Using contain when the element must size itself from outside content

contain: size and related containment modes can change how an element participates in layout. If you expect the browser to size the element based on its contents, size containment can produce surprising results.

Problem: The element may end up with an unexpected size because size containment prevents the element’s contents from influencing its own size in the normal way.

.panel {
  contain: size;
  border: 1px solid #999;
}

Fix: Use a less restrictive containment mode, such as layout or content, when you still want the element to size naturally.

.panel {
  contain: layout paint;
  border: 1px solid #999;
}

The corrected version keeps layout effects contained without forcing the element into size isolation.

Mistake 2: Forgetting contain-intrinsic-size with content-visibility: auto

When the browser skips rendering offscreen content, it still needs a reasonable placeholder size to avoid layout jumps.

Problem: Without an intrinsic size, the skipped element can collapse and then expand later, causing content to shift when it becomes visible.

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

Fix: Add contain-intrinsic-size so the browser reserves space before rendering.

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

The fix works because the browser has a fallback size even before the element is rendered.

Mistake 3: Expecting containment to hide overflow problems

contain is not a replacement for overflow. It isolates certain rendering effects, but it does not automatically clip visual overflow the way developers often expect.

Problem: Content may still visibly extend outside the element if you need clipping and do not set overflow behavior explicitly.

.tooltip-box {
  contain: paint;
}

Fix: Use overflow when clipping is required, and use containment for the rendering optimization you actually need.

.tooltip-box {
  contain: paint;
  overflow: hidden;
}

The corrected version separates clipping from containment so the behavior is predictable.

7. Best Practices

Practice 1: Use the least restrictive containment that solves the problem

Start with the smallest containment mode that gives you the performance improvement you need. Overusing size containment can make layout harder to reason about.

.widget {
  contain: layout paint;
}

This is often a safer default than applying every containment type automatically.

Practice 2: Pair content-visibility with a realistic intrinsic size

Whenever you skip rendering for offscreen content, reserve space that matches the expected content size as closely as possible.

.feed-item {
  content-visibility: auto;
  contain-intrinsic-size: 320px;
}

This reduces layout shift and makes the page feel stable as content appears.

Practice 3: Test scrolling and focus behavior

Because skipped content is not fully rendered until needed, always test keyboard focus, anchor navigation, and scroll-to-section interactions.

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

This helps you catch edge cases where content becomes visible later than expected.

8. Limitations and Edge Cases

If a page already performs well, adding containment everywhere may not provide a noticeable benefit and can make debugging harder.

9. Practical Mini Project

Let’s build a simple article feed where each post card is isolated, and offscreen posts are skipped until they scroll near the viewport.

.feed {
  display: grid;
  gap: 1rem;
}

.post {
  content-visibility: auto;
  contain-intrinsic-size: 280px;
  contain: layout paint;
  padding: 1rem;
  border: 1px solid #ddd;
  border-radius: 0.5rem;
}

.post h2 {
  margin: 0 0 0.5rem;
}

This example shows a realistic pattern for a long feed: each card is self-contained, and the browser can avoid rendering cards that are not yet visible. The placeholder size helps keep scrolling smooth and stable.

10. Key Points

11. Practice Exercise

Try styling a long FAQ page so that each answer block is isolated and offscreen answers are skipped until needed.

Expected output: A long page that scrolls smoothly, with each answer block appearing stable as it enters the viewport.

Hint: Give each answer block a realistic placeholder height so the page does not jump when content is revealed.

Solution:

.faq {
  display: grid;
  gap: 1rem;
}

.faq-item {
  contain: layout paint;
  border: 1px solid #e0e0e0;
  padding: 1rem;
}

.faq-answer {
  content-visibility: auto;
  contain-intrinsic-size: 240px;
}

This solution combines containment and rendering skipping in a way that is practical for long, scrollable content.

12. Final Summary

contain and content-visibility are CSS tools for controlling how much work the browser does. contain isolates an element’s effects, while content-visibility can delay rendering until content is needed.

They are especially useful on large pages with repeated components, long lists, and offscreen sections. The key is to apply them carefully, test the result, and pair skipped content with a sensible intrinsic size.

If you want to go deeper, the next useful topics are CSS rendering performance, layout containment details, and how browser painting and reflow work.