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:
- Rendering is an integral part of how modern websites work. It is a blocking operation, therefore it blocks all user interactions
- The results of rendering performance are incredibly apparent in mobile devices, especially the lower end ones
- 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.
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.
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.
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!