Using content-visibility to Pass INP And Improve Page Rendering

Last updated on Feb 15th, 2024 | 6 min

TL;DR: Leveraging the content-visibility CSS property is key to improving Interaction to Next Paint (INP) and page rendering speeds. This property allows browsers to skip rendering off-screen content until needed, significantly boosting performance and user experience by reducing load times and resource usage.


Do you want to know the secret to responsive web pages, smoother UX, and excellent INP score?

It’s efficient rendering work. 

That’s usually achieved by controlling the rendering of off-screen content, offloading the browser from executing tasks that aren’t immediately needed during the initial page load. 

In the following lines, you will learn how to leverage the content-visibility CSS property to significantly improve your site’s performance, boost your Core Web Vitals, and enhance user experience. 

Read on. 


Quantifying Responsiveness with Interaction to Next Paint (INP)

90% of a user's time on a page is spent after it loads.

Put another way, as crucial as investing efforts in speeding up your initial page load, it’s equally important how your website behaves once the user starts interacting with it. 

Is it glitchy? Does it provide smooth scrolling? Is it responsive? 

All the answers are hidden behind your INP score. 

Interaction to Next Paint in PSI

Interaction to Next Paint is a user-centric performance metric and successor to First Input Delay, used to evaluate a web page's responsiveness. It specifically measures the time it takes for a web page to respond visually to a user's input.

The keyword here is “visually”.

No one expects all your interactions to be executed in a couple of milliseconds. That’s just not possible. Everything you need to do to have a good INP score and Core Web Vitals is to provide immediate visual feedback to your visitors' actions on your website. 

Poor responsivess vs good responsiveness example

Circling back to the beginning of our article, ensuring that the browser can render your pages efficiently is the surefire way to achieving excellent scores and real-world experience. 

And the two critical factors that greatly impact your rendering speed are the main thread and your DOM size.  


The Role of the Main Thread and DOM Size

Let’s do a bit of housekeeping before delving into the technicalities.

Your INP score depends on how fast the browser returns visual feedback to the user after interacting with your website.

For the browser to be able to quickly receive, process, and present the feedback, its main thread must be offloaded of long-running tasks. 

Some of the biggest culprits include heavy JavaScript resources and, last but not least – large DOM size. 

This is the process. Now, let’s demystify each part. 
 

The Main Thread

The main thread is the primary thread of execution that handles most of the critical tasks associated with rendering a web page, including:

  • HTML, CSS parsing
  • JavaScript execution
  • construction of the Document Object Model (DOM)
  • adding computed styles
  • producing the layout tree
  • creating paint records 

Main thread tasks

When a task takes over 50ms to be executed, it’s considered a long task. This mainly happens because of heavy running JavaScript files or large DOM size.

You should know that the main thread can execute only one task at a time. So, the longer tasks occur, the more sluggish your website will appear. 

Why?

Imagine you have a web page with an interactive feature, like a complex data visualization or a dynamic user interface. The user triggers an action, say by clicking a button, which initiates a heavy JavaScript computation (it takes longer than 50ms to execute). 

This automatically poses several challenges to your site’s performance and responsivenss:

  1. This long computation blocks the thread, preventing it from performing other tasks like handling user inputs, rendering updates, or executing other scripts.
  2. While the computation is ongoing, the user might try to interact with other parts of the page - scrolling, clicking other buttons, or typing into input fields. However, these actions won't be processed immediately, and the page will appear to be frozen or unresponsive.
  3. If the user interaction was supposed to trigger a visual change (like highlighting a button when clicked), this feedback will be delayed until the long-running task is completed. 

In a nutshell, it’s critical to offload tasks from the main thread, and we know at least 7 ways you can do this. 
 

The DOM (Document Object Model) 

One of the main thread’s tasks is to parse the HTML. 

This means that the browser turns the data (HTML markup) into DOM.  

The DOM represents the page structure as a tree of objects that the browser uses to render content on the screen.

A larger DOM typically means more nodes (elements, text, comments, etc.) for the browser to manage.

DOM tree structure

When you have a large DOM size, the main thread has more work to do. It takes longer to parse the HTML into a DOM, apply CSS styles, layout the page, and re-render parts of the page when changes occur (like through JavaScript manipulations).

Large DOM trees can also slow down page interactions because every user interaction (like clicks, scrolls, and typing) often requires the browser to recompute styles and layout for parts of the DOM.

So, a good rule of thumb is for a page to have a DOM size of up to 1400 nodes. 

Good to know: Similar to the DOM tree, CSSOM (CSS Object Model) is a representation of the structure of cascading style sheets (CSS) as a hierarchical tree-like structure in memory. Having a lot and large CSS files can also negatively affect your site’s responsiveness and performance.


One way to improve the main thread efficiency and mitigate the impact of a large DOM is to control the rendering of offscreen content. 

And this CSS property can help you do it…
 

Leveraging content-visibility for Improved Rendering

The content-visibility CSS property is a groundbreaking addition to the toolbox of web performance optimization. 

This property, specifically in its auto setting, plays a pivotal role in enhancing the rendering efficiency of web pages. The content-visibility: auto attribute informs the browser that it can skip rendering and layout calculations for an element until it is needed, which is usually when the element enters the viewport.

When applied, content-visibility: auto allows the browser to optimize the rendering workload. By deferring the rendering of non-visible content, content-visibility significantly diminishes the initial load time and reduces the workload on the main thread, leading to faster rendering speeds and improved webpage responsiveness.

A practical example where content-visibility: auto shines is in optimizing Interaction to Next Paint. 

For instance, in a blog post with multiple comments or a long-scrolling news site, applying content-visibility: auto to individual comments or news articles not immediately in view ensures that the browser remains responsive to user interactions and loads visible content swiftly.

Here’s a simple way to implement it:

content-visbility example

Good to know: You also need to use contain-intrinsic-size to reserve space for the unrendered elements. This tells the browser what the rendered dimensions of the skipped content are. The more precisely you configure these values the smoother the experience at the end will be.


However, you should know that it’s not a one-size-fits-all solution. You must approach content-visibility with a balance, testing its effects across various devices and browsers to ensure consistent performance and avoid unforeseen layout shifts or accessibility issues.
 

Content-Visibility in Action

During our webinar with Google on “Optimizing INP”, we had the chance to demonstrate the impact of content-visibility and how NitroPack applies it. 

 

We identified the root cause for poor INP using several tools – Web Vitals extension, Chrome DevTools, and the Performance profiler. 

In the process, we discovered that the main culprits that caused an INP score of 272ms were two “Recalculate style” events that took 69.87ms to be processed and affected 1139 elements.

INP results without NitroPack

Enabling NitroPack on the website, our service automatically detected the elements that would benefit from content-visibility: auto. After a quick setup, we managed to reduce the render time of the long tasks and the number of affected elements more than twice:

INP score with NitroPack

Also, the INP score improved from “needs improvement” to “good.” 

Future-proof your website and pass INP on autopilot. Get NitroPack now →


Additional Tips for Optimizing INP

Undoubtedly, content-visibility offers large performance gains with minimal effort. 

However, in some cases, you might need extra performance power to guarantee smooth responsiveness and good INP scores. If that happens, here are several other INP optimization strategies you can utilize: 
 

1. Yield to the main thread

As you already know, your site’s performance heavily depends on how busy the main thread is. Yielding to the main thread refers to the practice of deliberately breaking up long-running tasks into smaller, manageable chunks to avoid blocking the main thread for extended periods. 

Yielding to the main thread

This can be achieved using yielding functions like:

 

2. Reduce your DOM size

The second mentioned responsiveness culprit was DOM size. Having a large DOM can significantly hinder passing INP. To prevent this, it's crucial to minimize its size or, more specifically, limit your DOM's depth.

This goal can be achieved through various strategies:

  • Steer clear of plugins and themes that are poorly coded.
  • Limit the use of JavaScript to create DOM nodes.
  • Opt for alternatives to page builders known for producing excessive HTML.
  • Consider dividing a single-page website into several pages.
  • Avoid concealing unnecessary elements with the display: none CSS property.
     

3. Avoid interaction overlap

Interaction overlap occurs when a user engages with another page element before the first interaction's rendering is complete. This often happens during rapid typing in form fields, where multiple keystrokes occur quickly.

To optimize this, consider:

  • Implementing debouncing on inputs to reduce event callback frequency.
  • Utilizing AbortController to cancel ongoing fetch requests, preventing main thread overload from excessive fetch callbacks.
     

Wrap up

Remember – INP is all about enabling the browser to paint the next screen as quickly as possible.

Users want to know that their actions are being processed and that something is happening in the background.

And mixing browser capabilities like content-visibility with powerful web performance solutions like NitroPack means you cover the entire user experience specter – from the initial load to browsing through all your pages. 

Test NitroPack for free →

Niko Kaleev
Web Performance Geek

Niko has 5+ years of experience turning those “it’s too technical for me” topics into “I can’t believe I get it” content pieces. He specializes in dissecting nuanced topics like Core Web Vitals, web performance metrics, and site speed optimization techniques. When he’s taking a breather from researching his next content piece, you’ll find him deep into the latest performance news.