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.
- contain creates containment boundaries for layout, paint, style, or size.
- content-visibility can delay rendering until the browser needs the content.
- Both features help performance on large or complex pages.
- Both are about reducing unnecessary work, not changing the visual design by themselves.
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:
- It can limit how far layout changes spread.
- It can prevent expensive repainting from escaping an element.
- It can reduce style recalculation in some cases.
- It can make scrolling and interaction smoother on large pages.
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
- Long article pages with many sections below the fold.
- Infinite feeds or large comment lists with many repeated items.
- Dashboard widgets that contain charts, tabs, or live data.
- Component libraries where each block should render independently.
- Accordion panels and collapsible sections that are not always visible.
- Complex documents where only the visible portion matters during initial load.
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
- content-visibility: auto is most effective on large, offscreen content blocks. Tiny elements often do not benefit much.
- Containment can change layout behavior, so it should be applied carefully to elements that participate in complex sizing.
- Some visual effects, such as shadows or positioned descendants, may behave differently when containment boundaries are introduced.
- Browsers still need to know the approximate size of skipped content, which is why contain-intrinsic-size matters.
- Containment does not replace semantic structure, good DOM organization, or efficient component design.
- Not every browser optimization is visible in the CSS alone; you often need to measure before and after.
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
- contain limits how an element affects the rest of the page.
- content-visibility lets the browser skip rendering work, especially for offscreen content.
- contain-intrinsic-size helps reserve space for skipped content.
- Containment is useful for large, modular UI pieces and expensive visual regions.
- Use the smallest effective containment mode to avoid layout surprises.
- Always test scrolling, focus, and size behavior after adding these properties.
11. Practice Exercise
Try styling a long FAQ page so that each answer block is isolated and offscreen answers are skipped until needed.
- Create a repeated FAQ item container.
- Apply content-visibility: auto to the answer block.
- Add contain-intrinsic-size to reserve space.
- Use contain: layout paint on each item to keep internal changes local.
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.