TL;DR / The contain property tells the browser that an element's internals are independent from the rest of the page, allowing the browser to skip work outside the contained subtree during layout, paint, or style recalculation.

How It Works

   Without containment             With contain: layout

    ┌──────────────┐                ┌──────────────┐
    │  Change in   │                │  Change in   │
    │    child     │                │    child     │
    └──────────────┘                └──────────────┘
            │                               │
            │                               │
            │                               │
            │                               │
            │                               │
            ↓                               ↓
  ┌──────────────────┐            ┌──────────────────┐
  │     Relayout     │            │     Relayout     │ rest of page
  │   entire page    │            │  container only  │ unchanged
  └──────────────────┘            └──────────────────┘

Edit diagram

CSS containment is a performance primitive. By declaring contain on an element, you make a contract with the browser: changes inside this element will not affect anything outside it. In return, the browser can optimize rendering by limiting the scope of layout, paint, and style calculations to the contained subtree.

Containment Types

The contain property accepts several values, each restricting a different rendering concern:

contain: layout — the element's internal layout does not affect external elements, and vice versa. The element establishes an independent formatting context. Changes to children's sizes or positions do not trigger layout recalculation of elements outside the container. The container's size is determined by its own CSS rules, not by its content.

contain: paint — the element's descendants do not paint outside its bounds. This is similar to overflow: hidden but applies at the rendering engine level. It also establishes a new stacking context and containing block for positioned descendants. The browser can skip painting this element entirely if it is off-screen.

contain: size — the element's size is not dependent on its children. The browser does not need to lay out the children to determine the container's dimensions. You must set explicit width and height because the element will report zero intrinsic size otherwise.

contain: style — scopes CSS counters and quotes to the subtree. This is the weakest containment and primarily prevents counter-related side effects from leaking out.

contain: strict — equivalent to contain: size layout paint style. The strictest containment, enabling all optimizations.

contain: content — equivalent to contain: layout paint style. Like strict but without size, so the container can still be sized by its content. This is the most practical default for most use cases.

How the Browser Exploits Containment

Without containment, the browser must assume that any DOM change could affect any other part of the page. A child element growing taller could push its siblings down, which could change the parent's height, which could affect the parent's siblings. The browser must re-layout the entire document to be safe.

With contain: layout, the browser knows that a child's size change is contained within the parent. It only needs to re-layout the contained subtree. For a page with 1000 DOM nodes where the contained subtree has 50 nodes, this reduces layout work by 95%.

Similarly, contain: paint lets the browser skip painting contained elements that are off-screen. Without containment, the browser may conservatively repaint off-screen elements because their painted output could overflow into the visible area. Paint containment guarantees this cannot happen.

The content-visibility Property

Built on top of containment, content-visibility is a higher-level property that automatically applies containment and can skip rendering of off-screen content entirely:

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

content-visibility: auto tells the browser to apply contain: content when the element is off-screen, and to skip layout, paint, and compositing for it entirely. This can dramatically reduce initial render time for long pages — the browser only renders what is visible in the viewport.

contain-intrinsic-size provides an estimated size for the element when its content is not rendered, preventing scroll bar jumps as elements enter and leave the viewport.

Real-World Impact

For a typical long-scrolling page with hundreds of sections, content-visibility: auto can reduce initial rendering time by 50% or more. Chromium's rendering tests show improvements from seconds to sub-100ms for pages with thousands of DOM nodes.

For widget-heavy dashboards, applying contain: content to each widget card ensures that updating one widget's data does not trigger layout recalculation for the entire dashboard.

For virtualized lists (like react-window or react-virtualized), CSS containment complements the virtual scrolling approach. The virtual scroller limits the number of DOM nodes, and containment limits the rendering work for each visible node.

Browser Support and Fallbacks

CSS containment is supported in all modern browsers. It is a progressive enhancement — browsers that do not support it simply ignore the property and render normally (with no containment-related optimizations but also no breakage). This makes it safe to adopt incrementally.

Gotchas

  • contain: size without explicit dimensions collapses the element to zero. The browser refuses to look at children for sizing, so the element must have width and height set. Forgetting this is the most common containment bug.
  • contain: layout creates a new stacking context and containing block. position: absolute children will be positioned relative to the contained parent, not the next position: relative ancestor. This can break existing layouts.
  • content-visibility: auto can affect accessibility when off-screen. Browsers have improved this (Chromium preserves the accessibility tree for auto elements), but older versions may skip off-screen content for screen readers. Test with real assistive technology to verify.
  • contain: paint clips overflow like overflow: hidden. If your design relies on visible overflow (like a tooltip or dropdown extending beyond its parent), paint containment will clip it.
  • Containment does not affect JavaScript measurements. getBoundingClientRect() and offsetHeight still return accurate values — the browser performs the necessary layout calculations on demand. Containment only skips speculative work during the rendering pipeline.