TBT Reduction — Total Blocking Time Optimization Guide
Reduce Total Blocking Time (TBT) to under 200ms. Learn how to identify long tasks, break up JavaScript execution, use code splitting, and optimize third-party scripts.
Detailed Explanation
Reducing Total Blocking Time (TBT)
TBT measures the total amount of time between FCP and Time to Interactive (TTI) during which the main thread was blocked by "long tasks." A long task is any task that takes more than 50ms. The blocking time is the portion over 50ms.
How TBT Is Calculated
Task Duration: 250ms → Blocking Time: 200ms (250 - 50)
Task Duration: 30ms → Blocking Time: 0ms (under threshold)
Task Duration: 80ms → Blocking Time: 30ms (80 - 50)
TBT = Sum of all blocking times = 200 + 0 + 30 = 230ms
TBT Thresholds
| Rating | Value |
|---|---|
| Good | ≤ 200ms |
| Needs Improvement | ≤ 600ms |
| Poor | > 600ms |
TBT as a Lab Proxy for INP
TBT is a lab metric (measured in controlled environments like Lighthouse), while INP is a field metric (measured on real users). However, TBT is strongly correlated with INP — optimizing TBT generally improves INP.
Identifying Long Tasks
Use Chrome DevTools Performance panel:
- Record a page load
- Look for red flags on tasks in the Main thread flame chart
- Any task bar with a red corner is a long task
- Click to see the call stack and identify the code responsible
Optimization Strategies
Code Splitting:
// Instead of importing everything upfront
import { heavyModule } from './heavy';
// Split into dynamic imports
const heavyModule = await import('./heavy');
Break Up Long Tasks:
// BAD: One long 200ms task
function processAll(items) {
items.forEach(item => expensiveOperation(item));
}
// GOOD: Yield between chunks
async function processAll(items) {
const CHUNK_SIZE = 10;
for (let i = 0; i < items.length; i += CHUNK_SIZE) {
const chunk = items.slice(i, i + CHUNK_SIZE);
chunk.forEach(item => expensiveOperation(item));
await new Promise(r => setTimeout(r, 0)); // yield
}
}
Tree Shaking: Ensure your bundler (webpack, Rollup, esbuild) eliminates unused exports. Large utility libraries like lodash should be imported selectively:
// BAD
import _ from 'lodash';
// GOOD
import debounce from 'lodash/debounce';
Defer Third-Party Scripts:
Move non-critical third-party scripts (analytics, ads, chat widgets) to load after the main content is interactive. Use async or defer attributes, or load them programmatically after TTI.
Use Case
TBT reduction is essential for JavaScript-heavy applications — SPAs built with React, Angular, or Vue, dashboard applications with complex initialization, and pages loading multiple third-party scripts. Lighthouse uses TBT as a significant scoring factor, so optimizing TBT directly improves your Lighthouse Performance score.
Try It — Web Vitals Reference
Related Topics
JavaScript Impact on INP — Main Thread Optimization
Optimization
INP Explained — Interaction to Next Paint Deep Dive
Core Web Vitals
Third-Party Scripts and Web Vitals — Impact and Mitigation
Optimization
FCP First Contentful Paint Guide — Measuring Initial Render
Supplementary Metrics
Core Web Vitals Scoring Thresholds — Complete Reference
Reference