Improve Rendering Performance With The New CSS ‘Content-Visibility’ Property

Learn about the content-visibility property

Earlier this year, Chromium 85 was released which low-key contained a new CSS property that completely flew by me at the time. The content-visibility property allows you to instruct the user-agent to skip all rendering work, including layout and painting until it is needed.

When Is This Useful?

If you have content that is initially hidden or off-screen then those sections, containing text or even multimedia, do not have to be rendered yet. This can dramatically improve page rendering performance since nothing is loaded, even the images or iFrames contained in the sections.

Right off the bat, I can think of several applications for this:

  • Everything below the fold on long pages
  • Dropdown menus
  • Side panels
  • Modals

How To Use It

The content-visibility property is basically a shorthand for CSS Containment Spec. The property has three unique values:

  • visible: this is the default value and yields no effect. The element’s contents are laid out and rendered as normal.
  • hidden: the element completely skips its contents. The content will not be accessible to user-agent features, such as find-in-page, tab-order navigation, nor will it be selectable or focusable. This is similar in effect to give the element’s contents display: none;.
  • auto: the element receives layout, style, and paint containment. If the element is not relevant to the user it also skips its contents. Unlike the hidden value the skipped contents will remain accessible to user-agent features such as find-in-page, tab-order navigation and must be focusable and selectable.

That means that we can use the following CSS to achieve the effect that a certain element won’t have its content rendered, painted, styled, or any of its media assets loaded until it is needed on-screen.

.element {
content-visibility: auto;

There is a small catch though. The height of the element will currently be 0 just like when you use display: none; on the element. This isn’t ideal since you can see the scrollbar shift as elements become visible, which is terrible UI.

.element {
content-visibility: auto;
contain-intrinsic-size: 1000px;

To solve this, we can use the contain-intrinsic-size property to set a placeholder width and height for the element until the actual content is needed and rendered.

Hiding Content With Content-Visibility: Hidden

.element {
content-visibility: hidden;

Using content-visibility: hidden; hides the content from the user’s view, removes it from user-agent features, and makes the content unselectable and unfocusable just like display: none; does. The difference lies in the cached rendering state.

When using display: none;it completely destroys the rendering state. This means that there is effectively no difference between removing display: none; from an element and rendering a completely new element with the same content.

When using content-visibility: hidden; however, the cached rendering state is maintained. This means that content can be rendered on the screen much more efficiently. This is great for blocks of content that can be shown/hidden repeatedly like with views in Single Page Applications.

Browser Support

The content-visibility was added in Chromium 85 and currently works on recent versions of Chrome, Edge, and Opera. Firefox has deemed it worth prototyping and Safari hasn’t said anything about this feature as of now.

This means that using content-visibility: hidden; on production sites comes with something to keep in mind. You can’t use content-visibility: hidden; safely as the only way to hide content. Either use some form of feature detection or combine it with display: none;.


This simple CSS property can bring exciting performance optimizations to your toolkit. For more information on resources content-visibility and the CSS Containment Spec check out the following resources:

Thank you for reading. If you have any thoughts on this, let me know in the comments below.

And you can follow me on Twitter here.


Your email address will not be published. Required fields are marked *