Total Blocking Time (TBT) is a lab performance metric that measures how long the browser's main thread is blocked by long-running JavaScript tasks during page load. It is the strongest lab predictor of whether real users will experience sluggish interactions, and it accounts for 30% of the Lighthouse performance score.
What TBT measures
The browser's main thread is responsible for executing JavaScript, calculating styles, performing layout, and painting pixels. It handles all of this sequentially — one task at a time. When a task takes longer than 50ms, the browser cannot respond to user input (clicks, taps, keystrokes) until that task finishes.
TBT sums up the "blocking portion" of every long task that occurs between two other metrics:
- Start: First Contentful Paint (FCP) — when the first content appears
- End: Time to Interactive (TTI) — when the page is reliably responsive
The calculation
For each task longer than 50ms, subtract 50ms. Add up the remainders.
Task A: 80ms → blocking time: 30ms (80 - 50)
Task B: 40ms → blocking time: 0ms (under 50ms threshold)
Task C: 200ms → blocking time: 150ms (200 - 50)
Task D: 60ms → blocking time: 10ms (60 - 50)
TBT = 30 + 0 + 150 + 10 = 190ms
A TBT of 190ms means the main thread was unavailable for 190ms during the page load window. During those milliseconds, a user clicking a button would experience a delay.
Why 50ms is the threshold
The 50ms limit comes from human perception research. Users perceive delays under 50ms as instant. Between 50ms and 100ms, they notice a slight lag. Above 100ms, the interface feels sluggish. Above 300ms, it feels broken.
By counting only the portion of each task above 50ms, TBT focuses on the time that actually impacts perceived responsiveness.
TBT thresholds
| Rating | TBT value | What it means |
|---|---|---|
| Good | Under 200ms | Users can interact without noticeable delay |
| Needs improvement | 200ms – 600ms | Some interactions feel slow, especially on mobile |
| Poor | Over 600ms | Users experience significant lag; rage clicks likely |
These thresholds apply to Lighthouse lab testing on simulated mobile devices with 4x CPU throttling.
TBT vs. FID vs. INP
Three metrics measure interactivity, each from a different angle:
| Metric | Type | What it measures | When it measures |
|---|---|---|---|
| TBT | Lab | Total blocking time during load | FCP → TTI |
| FID | Field (deprecated) | Delay of the first interaction | First user input event |
| INP | Field (current) | Delay of the worst interaction | Entire page lifecycle |
TBT and INP complement each other:
- TBT catches interactivity problems during development (in CI, in Lighthouse audits) before users encounter them
- INP reveals real-world interactivity problems that lab tests miss (runtime JavaScript, post-load interactions, real device constraints)
If TBT is good but INP is poor, the problem is likely post-load JavaScript (event handlers, dynamic content loading). If TBT is poor, fixing it will almost certainly improve INP as well.
What causes high TBT
1. Large JavaScript bundles
More JavaScript means more parsing and execution time on the main thread. A 500KB JavaScript bundle can take 200–400ms to parse and execute on a mid-range mobile device.
2. Synchronous initialization
Frameworks and applications that run all their setup code synchronously during page load create large monolithic tasks:
Framework init: 150ms ████████████████
State hydration: 100ms ██████████
Event binding: 80ms ████████
Total single task: 330ms → 280ms blocking time
3. Third-party scripts
Analytics tags, ad scripts, chat widgets, and A/B testing tools each add their own initialization tasks. Five third-party scripts adding 60ms each creates 50ms of total blocking time — and that assumes they do not trigger additional work.
4. Forced synchronous layout
JavaScript that alternates between reading and writing DOM properties forces the browser to recalculate layout on the main thread, creating long tasks that are not visible in the JavaScript profiler.
5. Heavy CSS processing
Pages with thousands of CSS rules or complex selectors require significant style recalculation time after the DOM changes, contributing to task length.
How TBT affects your Lighthouse score
TBT has the highest weight of any metric in the Lighthouse performance score:
| Metric | Weight |
|---|---|
| Total Blocking Time | 30% |
| Largest Contentful Paint | 25% |
| Cumulative Layout Shift | 25% |
| First Contentful Paint | 10% |
| Speed Index | 10% |
This means TBT has more influence on your score than LCP or CLS individually. A page with perfect LCP and CLS but 800ms TBT will score poorly.
How to measure TBT
Lighthouse (CLI or DevTools)
npx lighthouse https://example.com --only-categories=performance --output=json | \
python3 -c "import json,sys; d=json.load(sys.stdin); print('TBT:', d['audits']['total-blocking-time']['numericValue'], 'ms')"
Chrome DevTools Performance tab
Record a page load, then look at the "Total Blocking Time" metric in the summary. Click individual long tasks to see their source.
WebPageTest
WebPageTest reports TBT alongside a main thread breakdown that shows exactly which scripts and operations contribute to blocking time.
In code (approximation)
let estimatedTBT = 0
new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) {
estimatedTBT += entry.duration - 50
}
}
}).observe({ type: "longtask", buffered: true })
// Check after page settles
setTimeout(() => {
console.log("Estimated TBT:", estimatedTBT, "ms")
}, 10000)
Note: this gives an approximation. The Long Tasks API reports tasks over 50ms but with limited attribution detail compared to DevTools.
Quick wins for reducing TBT
- Defer third-party scripts — move analytics and widgets to load after the page is interactive
- Add
deferorasyncto script tags that do not need to block rendering - Code-split by route — ship only the JavaScript needed for the current page
- Remove unused JavaScript — audit your bundle and cut libraries that are not actively used
- Break up initialization — split large setup functions into smaller chunks with
setTimeout(fn, 0)between them
For a deeper walkthrough, see the reducing blocking time guide and the JavaScript performance guide.
Frequently Asked Questions
Why is TBT a lab-only metric?
TBT requires knowing when Time to Interactive (TTI) occurs, which is only determinable after the page has fully settled. In real-world usage, this moment varies based on user behavior, network conditions, and background tabs. Lab environments provide the controlled conditions needed for a consistent TTI measurement.
Does TBT matter for SEO?
Indirectly. TBT itself is not a direct ranking signal, but it strongly correlates with INP, which is part of Core Web Vitals (a confirmed ranking signal). Improving TBT in the lab almost always improves INP in the field.
Can images cause high TBT?
Images do not directly cause TBT because they decode on separate threads. However, JavaScript that processes images (resizing, canvas operations, lazy-load observers firing for many images simultaneously) can create long tasks that increase TBT.
What is the difference between TBT and TTI?
TTI measures when the page becomes reliably interactive (a point in time). TBT measures how much blocking occurred before that point (a duration). A page can have a fast TTI but high TBT if it has many moderate-length long tasks that resolve before a quiet window appears.
Should I optimize TBT or INP first?
Start with TBT because it is measurable in CI and provides faster feedback loops. Once TBT is under 200ms, shift focus to INP to catch post-load interactivity issues that TBT does not measure.