What is TBT?
TBT measures the total time the browser's main thread was blocked by long JavaScript tasks between First Contentful Paint and Time to Interactive. It is Lighthouse's closest proxy for INP and directly indicates how responsive a page will feel to user interactions.
Google's TBT thresholds
Good
≤ 200ms
Needs improvement
200ms – 600ms
Poor
> 600ms
What Total Blocking Time (TBT) means
Total Blocking Time (TBT) sums up the 'blocking portion' of every long task that runs on the main thread between FCP and Time to Interactive (TTI). A task is 'long' if it takes more than 50ms. The blocking portion is everything over 50ms — so a 120ms task contributes 70ms of blocking time to TBT.
TBT is a Lighthouse lab metric, not a field metric. It doesn't measure real user interactions — it measures how much main thread time was consumed by JavaScript during page load in a controlled test. This makes it a reliable predictor of poor interactivity, but not a substitute for real-user INP data.
High TBT is almost always caused by excessive JavaScript execution during load. Bundle splitting, code elimination, and deferred loading of non-critical scripts are the primary levers. In Next.js, the most common TBT sources are: large third-party scripts loaded synchronously, excessive client-side hydration, and React component trees that re-render during the initial mount phase.
Common causes of poor TBT
- Large JavaScript bundles that execute synchronously on the main thread during load
- Third-party scripts (analytics, chat, A/B testing) loaded without deferral
- Excessive React hydration work on pages with large client-side component trees
- Polyfills loaded for browsers that don't need them
- Unused JavaScript shipped to the client in the main bundle
- Heavy synchronous computations (sorting, filtering, parsing) running at page load
How to improve TBT
- Use next/script with strategy='afterInteractive' or 'lazyOnload' for all third-party scripts
- Analyse your bundle with @next/bundle-analyzer and eliminate unused imports
- Split large client components into smaller chunks with dynamic imports
- Move Server Components to RSC to eliminate their JS from the client bundle entirely
- Defer non-critical initialisation code with requestIdleCallback
- Use React Server Components to reduce the amount of JavaScript hydrated on the client
Frequently asked questions about TBT
What is a good TBT score?
Google Lighthouse considers a TBT of 200ms or less as 'Good'. Scores between 200ms and 600ms are 'Needs Improvement', and above 600ms is 'Poor'. For interactive apps with complex state, targeting under 100ms TBT is achievable with proper code splitting.
Is TBT the same as INP?
No. TBT is a Lighthouse lab metric that estimates main thread blocking during page load. INP is a field metric that measures real user interaction latency throughout the session. TBT is a useful predictor of poor INP, but they are different measurements. High TBT usually causes high INP, but reducing TBT alone doesn't guarantee good INP.
How do I reduce TBT in a Next.js app?
The most effective techniques are: move to Server Components to reduce client JS, use dynamic imports to split large components, defer all third-party scripts with next/script strategy props, and analyse your bundle to remove unused code. Each long task eliminated reduces TBT directly.
How does AuditJet surface TBT data?
AuditJet reports TBT on every Lighthouse audit as Google's best lab-based predictor of INP. When TBT rises above 200ms, AuditJet highlights the long tasks driving the regression — including third-party scripts and large JavaScript chunks — so your team can prioritise which bundles to split or defer next.
Monitor TBT continuously.
AuditJet tracks TBT across all your pages on a scheduled basis and alerts your team the moment it regresses.