How we brought a website’s Total Blocking Time below 50 ms with Lazy Hydration

What is SSR?

SSR stands for server-side rendering. Roughly speaking, it consists of pre-generating a page on the server before it reaches the client's computer.

Why use SSR?

While SSR mainly improves SEO results, this technique can also improve page loading times. The generated page will reach the user in the right form before any javascript line is executed on his computer.

Drawbacks of server-side

The main downside of SSR is the time it takes for the user’s machine to hydrate on the client side.

What is hydration?

Hydration occurs on the user’s computer for websites that undergo server-side generation.

This process is based on connecting dynamic events to static html elements via javascript. For example, consider a button, which in HTML is represented by a < button > tag. After receiving a page from the server, the button is not interactive, meaning that if the user clicks on it, nothing will happen. The appropriate action in JavaScript will only be triggered after hydration takes place and the appropriate event is connected to the button.

Why is hydration expensive?

At first glance, the < button > example above doesn’t make hydration seem too complicated in a computational sense. You just need to find the right button and assign an event to it. However, problems arise if the page has several thousand similar elements, all of which contain complex conditions that are dependent on each other, such as on Reffine websites.

Of course, modern computers and smartphones cope with such tasks in less than a few hundred milliseconds. However, this level of complexity and the need to perform hydration on so many elements can rapidly scale up the cost as the amount of computing power needed to render the page rises.

Total Blocking Time

A few hundred milliseconds isn’t much, right?

Well, it becomes a bigger issue when we take the interactivity of the website into account. When a user visits our website, he or she expects to be able to immediately interact with any elements that appear.

Total Blocking Time (TBT) is a metric that determines how long the page was blocked for the user, during which time interaction was not possible. According to google: “TBT measures the total time between First Contentful Paint (FCP) and Time to Interactive, where the main-thread was blocked for long enough to prevent input responsiveness”.

The main thread is where a browser processes user events, paints, and anything else we refer to as tasks. When any one of these tasks runs for longer than 50 ms, they become known as a Long Task and the main-thread is considered blocked because the browser cannot interrupt a task in progress. So, if we have five long tasks and each of them lasts 100 ms, the following equation will determine the TBT metric: (100ms-50ms)5=250ms.

While the ideal TBT is, of course, 0 ms, the average result is 600 ms. Meanwhile, if a website’s TBT climbs above 3000 ms, this typically means that something has gone wrong. Returning to our example, if we could break those five long tasks into 10 tasks that are 50 ms each, we could bring the TBT to 0.

Block hydration on Reffine websites

So far, our blocks have been hydrated in one long task. This means that if we had 10 blocks on the main page, then the task corresponding to their hydration lasted as long as the hydration of all 10 blocks. As a result, on many pages this task exceeded 50 ms.

Lazy Hydration

Lazy hydration is a postponement of the hydration task. In this case, instead of hydration occurring immediately after loading the page, the process starts only after a specific action. This can be user input, such as a click, scroll, or mouse movement, but also a timeout or, as in our case, the browser becoming idle.

Request Idle Callback

In javascript, we have access to the requestIdleCallback method, which allows us to queue the task until the browser is in an idle state.

The latest changes in the hydration of Reffine blocks

All the above information allowed us to break one long task into smaller tasks with block hydration and execute them one by one when the browser was idle. We managed to separate the hydration of individual components and run it block by block.

For example, if we have 3 blocks on the page Hero Slider, Snippet and Content Blocks, the first action to take place is queuing the hydration of the first block - the Hero Slider. After the browser is idle, the hydration of the Hero Slider will take place. Afterwards, the snippet’s hydration will be queued and, after its execution, content block hydration is scheduled and executed during a browser’s idle periods.

As a result, instead of one long task lasting ~75 ms, we have three tasks that last 25 ms each.

If you have any questions about Lazy Hydration or any other website speed related topics drop us a note!