Boost Your Rendering Performance with content-visibility

cover-image.png

Developers don’t typically prioritize rendering performance when they aim to improve and optimize their websites.

After all, there are other optimizations such as improving server response times, reducing file sizes, and prioritizing the file loads that provide immediate visible improvement. Also, rendering is an internal process of browsers that web developers do not have direct access to.

However, developers should focus on rendering for three primary reasons:

  1. Rendering is an integral part of how modern websites work. It is a blocking operation, therefore it blocks all user interactions
  2. The results of rendering performance are incredibly apparent in mobile devices, especially the lower end ones
  3. There are direct and indirect ways to help browsers render our content more efficiently

Influencing browser rendering processes is now even easier to do, thanks to the new CSS content-visibility property. Essentially, this property changes the visibility of an element and manages its render state.

It is somewhat similar to display and visibility properties that already exist. However, content-visibility operates differently than these do.

In this blog post, we'll learn about content-visibility, how it helps with rendering performance, and. how it compares to display and visibility properties.


How content-visibility improves rendering performance

The key capability of content-visibility is that it allows us to postpone the rendering of the HTML elements of our choice. By default, browsers render all the elements within the DOM tree that can be viewed by the user.

Users can see the elements that fit into their viewport and view the other elements within the page by scrolling. Rendering all the elements at once allows the browser to calculate the dimensions of a page correctly while keeping the page layout and the scrollbar consistent throughout the page.

If the browser didn't render some of the elements within the page, scrolling would be a nightmare because it wouldn’t be possible to calculate the page height correctly. Or would it?

Fear not. content-visibility has an auto option that detects whether an element is within the viewport of the user and skips the rendering for the elements that haven’t yet enter the viewport yet.

This makes sense because the user will not initially see the elements that stay outside of their screen, meaning that these elements are unnecessary during the initial page load. So, postponing rendering these elements reduces the initial rendering time so a user can see the content faster.


Measuring the power of content-visibility

Sure, it all sounds good in theory, but let’s dig deeper. So, to leverage the power of content-visibility and measure the benefits as accurately as possible, I've put together a demo blog.

A blog is a good test case for this because it has text, images, and various other HTML elements. Blog pages tend to be long, so there is often some content below the fold that can benefit from delayed rendering. And the content is usually static, so we don’t need to account for any dynamic content being loaded that can affect our metrics. We can simply focus on the initial page load.

With these ideas in mind, I've modified the blog to create two different versions of it with the same content, except for one important difference: one of them has content-visibility: auto turned on.

I also decided to run the benchmarks on Chrome's low-end mobile phone emulation. There is simply too much processing power on a modern high-end laptop to see the difference between the two versions clearly. I ran multiple benchmarks with similar results. Below, you can see an example benchmark for each version of the blog.

s_B3A5D149F29A71ABB28423E2EA207B42EB23671EC4541464C3CC7FA33FDC1CF3_1602021803990_benchmark+without.png

s_B3A5D149F29A71ABB28423E2EA207B42EB23671EC4541464C3CC7FA33FDC1CF3_1602021808242_benchmark+with.png

The benchmarks show that the rendering takes about 50MS shorter when content-visibility is used. It is quite an improvement that mobile users would appreciate.

Both versions of the blog are available online so you can run the benchmarks yourself as well:


How to implement content-visibility

Reaping the benefits of content-visibility isn't hard. We first start by identifying the parts of the page we want to use content-visibility on. On the screenshot below, you’ll see that I've identified the content that is visible to the user immediately (i.e., above-the-fold content) and the content that is reachable by scrolling. Postponing the rendering of the below-the-fold content would reduce our initial rendering time.

s_B3A5D149F29A71ABB28423E2EA207B42EB23671EC4541464C3CC7FA33FDC1CF3_1602022041730_page+sections.png

Setting content-visibility: auto for the below-the-fold content would trigger the render optimization functionality where the browser postpones the rendering for that content until it is visible. Here’s the code to do so:

.below-the-fold {
  content-visibility: auto;
}

This achieves the rendering behavior we want but has one small issue. Remember how I mentioned that rendering all the content at the beginning was needed to ensure that the page height was calculated correctly and to keep scrolling consistent? Now we have this issue here.

By default, content-visibility will treat the height of the element it's assigned to as 0. The browser will make this element invisible by making its height 0 until it is rendered, which messes with our page height and scrolling.

But this behavior is overridden if there is already a height assigned to the element or its children elements, so this isn’t an issue if your below-the-fold elements already have height properties set.

If you don’t have height properties in your elements and do not want to add them because of possible side effects, you can use the contain-intrinsic-size property to make sure the elements are rendered correctly while also keeping the benefits of delayed rendering. With this, we end up with a code like below:

.below-the-fold {
  content-visibility: auto;
  contain-intrinsic-size: 240px;
}

The value 240px you see there is just an example. That should be replaced with the actual height of the element we'll use content-visibility on.

Unfortunately, calculating a single value for the whole below-the-fold content is difficult. The page can be long or short based on the items shown on that page. So, we'll add this property to the elements that have a predictable height. For example, the articles on our example blog have a pretty standard look.

s_B3A5D149F29A71ABB28423E2EA207B42EB23671EC4541464C3CC7FA33FDC1CF3_1602022246866_chunks.png

Each article in the blog has 468px height. Now we can complete our example case by setting contain-intrinsic-size to 468px.

.below-the-fold {
  content-visibility: auto;
  contain-intrinsic-size: 468px;
}

Advanced usage of content-visibility

The auto option certainly does wonders, but it’s possible to take things a step further. content-visibility provides us with two more potential values called hidden and visible. These values do what you would expect them to do and hide or show the element that content-visibility is assigned to. These values can be useful for advanced use cases.

One such case would be showing/hiding elements programmatically, similar to how the display property is used. In this case, content-visibility can improve the rendering performance for items that are shown or hidden frequently, such as modals or pop-ups. content-visibility can provide this performance boost thanks to how its hidden value functions differently than others.

How content-visibility: hidden compares to alternatives

display: none: This completely hides the element and destroys its rendering state. When we want to show the element again, the browser has to re-render it, which is expensive.

visibility: hidden: This simply makes the element invisible. The browser can re-render it when it deems necessary, even if the element is hidden. The element and its children also keep a visibly empty space in the page.

content-visibility: hidden: This hides the element but keeps its rendering state. This means the element behaves as it does on display: none, but the cost of showing it again is much lower.

Of course, this doesn't mean we don’t need display or visibility anymore. They still have their use cases, but now, we have an additional tool to leverage.


Browser support for content-visibility

Because content-visibility is still in the working draft stage, its support will come a bit slower than others. Currently, only Chrome and Edge version 85 support it. This means it will take a bit longer for us to get the full benefits of implementing this optimization.

On the flip side, the auto functionality of this CSS property is purely for performance optimization. It doesn't cause any visual change. When it is not supported by a browser, it will simply be ignored without causing any negative impact, so it’s safe to implement for all browsers and observe the behavior as it gets supported by more browsers.

You can check the current browser support here.


Conclusion

content-visibility promises solid gains for very little work. The auto functionality is something we can implement today and start reaping benefits from. More advanced usage of it as a replacement for display or visibility, however, has to wait until content-visibility becomes supported by most modern browsers.

Give content-visibility: auto a try and let us know how it performs for you!