/ โ† Core Web Vitals Fixes / Total Blocking Time
In this guide
  1. How Total Blocking Time is calculated
  2. Why TBT matters when it is not a Core Web Vital
  3. Finding the scripts behind the blocking time
  4. Third-party tags
  5. Your own JavaScript
  6. Platform notes
  7. The fix, step by step
  8. Stopping blocking time creeping back
  9. Questions and answers

How Total Blocking Time is calculated

A browser does most of its work on a single main thread: parsing HTML, running JavaScript, working out styles and layout, and responding to taps and clicks. While a piece of JavaScript is running, anything else has to wait. Any continuous chunk of main-thread work that lasts longer than 50 milliseconds is called a long task.

TBT only counts the part of each long task beyond the first 50 ms, because a delay under 50 ms is too short for a person to notice. It adds those excess amounts together for the period between First Contentful Paint and the point where the main thread becomes reliably quiet.

Long task lengthCounted as blocking time
40 ms0 ms (not a long task)
120 ms70 ms
300 ms250 ms
Total for these three320 ms

This is why one 400 ms task is much worse than eight 50 ms tasks doing the same work: the single long task contributes 350 ms of blocking time, the eight short ones contribute nothing. Most TBT work is therefore about doing less and doing it in smaller pieces.

In the Lighthouse report for mobile, TBT of 200 ms or less is shown in green, 200 to 600 ms in amber and over 600 ms in red. Those bands are Lighthouse's own scoring, not a Google ranking threshold.

Why TBT matters when it is not a Core Web Vital

Lighthouse cannot measure Interaction to Next Paint because nobody taps or clicks during a lab test. TBT is its substitute. A page with lots of long tasks during load is likely to respond slowly when real visitors try to open the menu, tap "add to basket" or type in a search box while scripts are still running.

The link is not exact. TBT only looks at the loading period, and INP covers interactions throughout the whole visit, including slow event handlers long after load. A page can have low TBT and still fail INP because one button runs an expensive handler, and a page can have high TBT but pass INP if visitors rarely interact during the first few seconds. Use TBT to spot heavy start-up JavaScript, and use field INP data to decide whether real visitors are affected. The INP guide covers interaction handlers in depth.

TBT also carries the largest weight of any metric in the Lighthouse performance score in recent versions, so reducing it is usually the quickest way to lift that score. Remember that the score is a lab summary; the Core Web Vitals pass or fail comes from real users.

๐Ÿ’ก Run Lighthouse three to five times and use the median. TBT varies more between runs than most metrics because it depends on how busy the test machine's CPU is.

Finding the scripts behind the blocking time

Start with the lab section of the Core Web Vitals checker or a Lighthouse report. The diagnostics that list long main-thread tasks, JavaScript execution time per script and the impact of third-party code are your first map. Note the scripts that appear near the top of each list.

Recording a trace

  1. Open Chrome DevTools, go to the Performance panel and set CPU throttling to a 4x or 6x slowdown to imitate a mid-range phone.
  2. Click the reload-and-record button. When it finishes, look along the Main track for grey task bars marked with a red corner. Each one is a long task.
  3. Click a long task and open the Bottom-Up tab, grouped by domain or by URL. This shows which files and which functions used the time.
  4. Look at the Summary for each task: time split between scripting, rendering and painting. Heavy "Recalculate style" or "Layout" inside a task suggests a script that is changing the page in a loop.

Grouping by owner

Sort what you find into three groups: third-party tags (analytics, advertising, chat, reviews, heatmaps, consent tools), your own framework and application code, and platform or plugin code you did not write. Each group has a different fix, and on many small-business sites the third-party group is the largest by a wide margin.

// Paste into the console, then reload, to log long tasks as they happen
new PerformanceObserver((list) => {
  for (const task of list.getEntries()) {
    console.log('long task', Math.round(task.duration), 'ms at', Math.round(task.startTime));
  }
}).observe({ type: 'longtask', buffered: true });

The newer Long Animation Frames API gives richer detail, including the script source for each slow frame, and is what the web-vitals library uses for INP attribution in the field.

Third-party tags

Every tag you add runs on the same main thread as your site. Tag managers make it easy to add tags and hard to remember why each one is there.

// Load a chat widget only when the visitor asks for it
document.querySelector('#chat-button').addEventListener('click', () => {
  const s = document.createElement('script');
  s.src = 'https://chat.example.com/widget.js';
  s.async = true;
  document.head.appendChild(s);
}, { once: true });

The third-party tags guide goes further into facades, consent-based loading and moving tags to server-side tagging.

Your own JavaScript

Ship less on first load

Break up what remains

Work that genuinely has to happen during load can be split so the browser can respond between pieces. Yielding hands control back to the main thread briefly:

function yieldToMain() {
  if (globalThis.scheduler?.yield) return scheduler.yield();
  return new Promise((resolve) => setTimeout(resolve, 0));
}

async function initWidgets(widgets) {
  for (const widget of widgets) {
    widget.init();
    await yieldToMain();
  }
}

scheduler.yield() is not yet available in every browser, so feature-detect it and fall back to setTimeout as above. For heavy calculations that do not touch the page, such as parsing a large data file or filtering thousands of products, move the work into a Web Worker so it runs off the main thread altogether.

โš ๏ธ Adding defer or async to a script changes when it downloads and starts, not how long it runs. It can improve First Contentful Paint, but a 500 ms task is still a 500 ms task. To reduce TBT the work itself has to shrink, move or be split.

Platform notes

The fix, step by step

Step 1

Record the load and list the long tasks

Run Lighthouse on mobile settings three to five times and note the median TBT, then record a page load in the DevTools Performance panel with the CPU slowed by four to six times. Mark every long task on the main track between the first paint and the point where activity stops. Write down the start time and length of each so you can compare the same list after every change.

Step 2

Attribute each task to a script

Click each long task and use the Bottom-Up view, grouped by domain or URL, to see which file and function used the time. Sort the results into third-party tags, your own application or framework code, and plugin or platform code. Add the size of each file from the Network panel. The biggest few items on this list are where nearly all of the reduction will come from.

Step 3

Remove scripts nobody needs

Go through the tag manager, theme and plugin list and remove every script without a clear current owner and purpose: ended campaigns, duplicate analytics, abandoned heatmaps, old chat tools and uninstalled apps that left code behind. Deleting a script is the only change that removes its cost entirely, so do this before any optimisation, and keep a record of what you removed in case something needs restoring.

Step 4

Delay third-party tags until they are needed

Set advertising and analytics tags to fire only after consent has been given. Replace chat widgets, embedded videos and review carousels with lightweight placeholders that load the real script on click or when scrolled into view. For tags that must run for everyone, trigger them after the load event rather than immediately, and check in the Performance panel that the long tasks have moved out of the loading period.

Step 5

Ship less JavaScript on first load

Run a bundle analyser on your own code and split large bundles by route and by feature with dynamic import(). Replace heavy libraries with smaller ones or native browser features, stop importing whole icon or utility packages, and update your browser targets so modern browsers are not sent unnecessary polyfills. On framework sites, reduce how much of the page hydrates and keep static sections as plain HTML.

Step 6

Break up the tasks that must stay

For start-up work that cannot be removed or delayed, split loops and initialisation into smaller chunks and yield to the main thread between them, using scheduler.yield() where it exists and a setTimeout fallback elsewhere. Move pure calculation, such as parsing or sorting large data sets, into a Web Worker. Aim for each remaining task to finish well within about 50 milliseconds.

Step 7

Retest under the same conditions

Rerun Lighthouse the same number of times, on the same settings, and compare the median TBT and your long-task list with the baseline. Check that features still work, particularly consent, checkout and forms. Because TBT is lab-only, confirm the real-world effect by watching field INP in the Core Web Vitals checker and your own monitoring over the following weeks, since that is the figure Google assesses.

Stopping blocking time creeping back

For a closer look at splitting work up, see the long tasks guide.

Questions and answers

1. What is a good Total Blocking Time?

Lighthouse shows TBT in green at 200 milliseconds or less on its mobile test, amber from 200 to 600 milliseconds and red above that. These are Lighthouse scoring bands rather than a Google ranking threshold, because TBT is a lab measurement and is not one of the Core Web Vitals. Treat 200 milliseconds on a throttled mobile test as a sensible target, and judge real-world responsiveness by field INP, which should be 200 milliseconds or less.

2. Is TBT a Core Web Vital?

No. The Core Web Vitals are Largest Contentful Paint, Interaction to Next Paint and Cumulative Layout Shift, all measured from real Chrome users. TBT is only available in lab tools such as Lighthouse and PageSpeed Insights, where it stands in for INP because no one interacts with the page during a test. It is worth fixing because high TBT often goes with poor INP and because it strongly affects the Lighthouse performance score, but it does not decide a pass or fail on its own.

3. Why is my TBT different every time I run Lighthouse?

TBT depends on how fast the test machine processes JavaScript, and that varies with whatever else the machine is doing, including other browser tabs and extensions when you test locally. Third-party scripts that load different adverts or experiments on each visit add more variation. Run the test three to five times and use the median, test in an incognito window without extensions, and compare before and after results using the same tool and settings.

4. Will adding defer to my scripts reduce TBT?

Usually not by much. The defer attribute changes when a script downloads and runs, which can speed up the first paint, but the script still does the same amount of work on the main thread once it starts. If it runs a 300 millisecond task, that task still counts. To reduce TBT you need to remove the script, delay it until after load or until the visitor needs it, make it do less, or break its work into smaller pieces.

5. Do third-party scripts count towards my TBT?

Yes. Every script on the page shares the same main thread, so time spent running analytics, advertising, chat, review or consent scripts counts exactly the same as your own code. On many small-business sites third-party tags make up the largest share of blocking time. Lighthouse has a diagnostic that estimates the main-thread time used by each third-party provider, which is a good way to see which tags cost the most before deciding what to remove or delay.

6. My TBT is high but my field INP is good. Should I still fix it?

It is lower priority. Field INP is what Google assesses and what visitors feel, so if it is comfortably under 200 milliseconds at the 75th percentile, real visitors are not suffering much. It is still worth trimming obvious waste, because high TBT often shows up as poor INP on slower phones or after you add another script. Check the mobile field data specifically, since desktop visitors on fast machines can hide a problem that phone users face.

7. Can a Web Worker fix high TBT?

For the right kind of work, yes. A Web Worker runs JavaScript on a separate thread, so heavy calculations there do not block the main thread and do not count as long tasks. It suits pure data processing, such as parsing, sorting, filtering or compressing. It cannot touch the page directly, so code that updates the DOM must stay on the main thread. Libraries exist that run some third-party tags in a worker, but test them carefully, because not every tag works that way.

Find the scripts blocking your page

The Core Web Vitals checker runs a Lighthouse lab test next to real-user field data from the Chrome UX Report, and points to the scripts and code behind heavy main-thread work and slow responsiveness.

Open Core Web Vitals Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Core Web Vitals Fixes  ·  Poor Interaction to Next Paint  ·  Long Main-Thread Tasks  ·  Third-Party Tags  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.