What Is TBT (Total Blocking Time) in Lighthouse and Why It Matters

Before writing this post, I was thinking about the fact that we still use the term “web page.” Ideally it refers to a static layout of content, say, text and images like in a book, and that was the case back when the internet started.

But the state of the art in web development dictates its own rules, turning websites into full-featured applications. With scripting you can enhance your website with effects, modals, data processing, etc. While that may sound like an endless field of opportunities, overdoing it may cause a website to feel sluggish or even become irresponsible to user input.

What Is Total Blocking Time?

TBT does this by measuring the total amount of time between First Contentful Paint and Time To Interactive and finding long tasks within the aforementioned timespan.

Lighthouse performance report including Total Blocking Time.

To determine TBT, Lighthouse compares your results to the top 10,000 websites loaded on mobile devices. Here’s the distribution:

  • 0–300 ms — Green, fast
  • 300–600 ms — Orange, moderate
  • More than 600 ms — Red, slow

It’s worth mentioning that Total Blocking Time has a 25% impact on the overall score on mobile devices, making it an important metric to improve. You can see it yourself when changing values in the Lighthouse Scoring Calculator.

What Is a Long Task?

A JavaScript task that keeps the main thread busy for more than 50 milliseconds is considered a long task.

However, the “blocking” time of the task is everything that is above the 50-millisecond threshold. Therefore, the sum of the blocking time for each long task between FCP and TTI is the Total Blocking Time.

Let’s consider the following timeline as an example:

Durations of tasks in the main thread.

While there are 5 tasks in the example above, only 3 of them are long tasks. So, to calculate the blocking time, the browser will take overheads of each of them and combine into the total value, like on the picture below:

The light blue parts are considered blocking here.

Summing up everything, we see that it took the browser 560 milliseconds to process everything, and only 345 milliseconds of that time is considered blocking time.

The Math Behind TBT

There could be other work being done in addition to the current input handling. For example, there could be a task, but not a long one, running when the input happens. This means that we should take into account an additional potential 50 milliseconds (occupied by a previous task), hence only 50 milliseconds left for the script to respond to the user input.

A visual representation of a 100ms window.

Example: Finding Long Tasks on the Page

A visual representation of a page loading process. We will be looking at the tasks.

Tasks (shown in grey) are long tasks if they have red flags. Under each task you can see a stack-like list of functions that call each other (from top to bottom). Click on them to find detailed info about the pieces of code that are causing issues. When talking solely about TBT, you can disregard long tasks that happen prior to the FCP event here, since TBT is measured starting from First Contentful Paint.

Speaking of FCP, I once found this confusing during my career. The intermediate goal was to improve First Contentful Paint. Once that had been done and FCP became green, TBT immediately raised into the red zone. That happened because all the tasks that were happening before FCP were now influencing Total Blocking Time. I always advocate for a complex approach when it comes to optimizing websites, and this was a real-life example of that.

Now that we know how to identify long tasks, let’s talk about fixing them.

How to Improve Total Blocking Time

  • Reduce the impact of third-party code
  • Reduce JavaScript execution time
  • Minimize main-thread work
  • Keep request counts low and transfer sizes small

Reduce the impact of third-party code

Find detailed info on what external scripts are slowing down your page in this section.

Network and Script Evaluation aspects of third-party code.

Reduce JavaScript execution time

  1. Network. If an asset (a script in this case) is optimized but served in an inefficient way, it may affect execution time. For example, a good choice would be using a CDN for JS libraries instead of hosting them on your own (for example, jQuery for legacy websites).
  2. Parsing and compilation. The more code you have, the more prep work a browser will need to do. Consider splitting code into smaller logical chunks and defer actions that are not needed right away.
  3. Execution. Even a small script may take a long time to finish if it’s not optimized enough.
A detailed breakdown of how long it takes a browser to process each script.

All of this affects the time it takes for a script to give control back to the page to respond to user input. This one is closely related to the previous section.

Minimize main-thread work

Here you can see that “Script Evaluation” can be greatly improved.

This tab gives you info about what keeps the main thread busy. Use this as a guideline on what direction you should be moving in. If “Script Evaluation” is suspiciously high (like in the screenshot above), I recommend going to the Performance tab in DevTools and running the profiler.

Real-life example: I once had a WordPress website that suffered from large TBT values. After looking at this tab and at the Performance tab, I found out that the forms plugin’s rendering process on the website was JavaScript-driven, and most of that JS was executed on the page load even if the forms were hidden in modals. Implementing lazy loading and dynamic script injection helped dramatically.

Keep request counts low and transfer sizes small

A detailed breakdown of resources that are requested by the page.

If your scripts are render-blocking, your website’s responsiveness may heavily depend on visitors’ network conditions. That’s why it’s important to defer secondary scripts and load them on demand instead of pulling everything at once. It’s not possible to do this in 100% of cases but it’s better to keep in mind when designing your code architecture.

Optimizing images

Why am I mentioning this if so far we’ve been mostly talking code? Here’s the reason: images load in a non-blocking manner. That means they won’t block the main thread itself, but they may reserve bandwidth and affect the speed of loading script assets, which have a much greater impact on TBT. That’s why I’d recommend taking care of images as well, keeping them optimized and delivered effectively.

To accomplish this in a convenient automated way, Uploadcare created a tool called Adaptive Delivery. It allows you to:

  • Defer image loading (aka “lazy loading”). Images load automatically once they enter the viewport.
  • Apply image transformations.
  • Optimize parameters like size, format, quality and dimensions automatically based on the user’s device configuration.

With the last one, you won’t need to manually prepare different image versions. The tool is content-aware and AI-driven, so the optimal quality will be determined based on the image content.

Also, it’s a tiny (3.7 KB) compressed script, which may save you sometimes megabytes of image data and do all the heavy lifting on the front end.

If you want to look at the potential results Adaptive Delivery can provide in your particular case, test your website with PageDetox, another service from Uploadcare that can generate reports based on image size data.

PageDetox tells you how fast your website can be if you use Adaptive Delivery.

Conclusion

To keep your website healthy, I’d first recommend being constantly aware of its performance, wisely choosing new plugins, libraries and services, and monitoring how they affect scores. While there are situations where tweaking a couple lines of code may solve a problem quickly, a systematic and user-centric approach is a more universal strategy of maintaining good scores in the long run.

File system as a service for web and mobile apps