A WordPress page can look finished while its menu ignores a tap. The browser may be busy executing JavaScript, calculating layouts, or processing a large block of work.
Total Blocking Time, or TBT, helps expose that loading-time congestion in a lab test. It also gives you a starting point for tracing the code responsible. Improving it requires more than making JavaScript files smaller or moving them into the footer.
The useful sequence is to reproduce the blocking, identify its owner, and remove or divide the unnecessary work. Then test the interactions your visitors actually use.
Understand what TBT counts
The browser’s main thread handles work needed for page rendering and many interactions. A task lasting more than 50 milliseconds is a long task. TBT counts the portion above that threshold during its measurement window.
For example, a 240 ms task contributes 190 ms of blocking time. A 35 ms task contributes nothing. Three tasks lasting 80, 35, and 240 ms therefore contribute 220 ms altogether.
Those are illustrative durations. The calculation is 30 + 0 + 190, rather than the combined duration of all three tasks.
Google’s TBT definition describes Lighthouse’s default window from First Contentful Paint to Time to Interactive. Other measurement modes can use different windows. Check the report’s method before comparing numbers from different tools.
A TBT below 200 ms is the commonly cited good target for average mobile hardware. Treat it as a diagnostic benchmark under specified test conditions. A powerful desktop running without throttling can conceal work that blocks a slower phone.
TBT is not a Core Web Vital. Interaction to Next Paint, or INP, evaluates responsiveness to interactions across a page visit. A low loading-time TBT cannot prove that a filter or checkout button responds quickly later.
Our Core Web Vitals metrics guide explains how to interpret those measurements together.
Build a baseline you can reproduce
Choose a URL where the issue occurs, then describe the visit precisely. Include the browser, device profile, network settings, login state, and consent state. Also note whether browser and server caches are warm or cold.
On a shop, test a product page with realistic variations and reviews. An empty demonstration product might skip the expensive behavior. On a publication, include a representative article containing the embeds and advertising integrations readers encounter.
Use Lighthouse to establish a repeatable loading test. Run it several times using the same configuration. Save the median, the range, and the original reports rather than keeping only the best score.
Avoid testing through a browser crowded with unrelated extensions. Keep other heavy workloads out of the comparison. When investigating authenticated behavior, use a dedicated test account with the relevant permissions and representative data.
Next, record the same page in Chrome DevTools’ Performance panel. Use a reload recording for startup work and a separate interaction recording for the delayed control.
These recordings answer different questions. The first reveals what blocks loading. The second shows what happens when a visitor changes a variation or submits a search.
Trace a long task back to its owner
Select a visibly long task in the main-thread track. Inspect the calls inside it, then narrow the recording to that interval. This prevents unrelated activity from dominating the analysis.
The Bottom-up view aggregates activities within the selected interval. Self Time counts work directly inside an activity. Total Time also includes its descendants.
That difference prevents a common mistake. A wrapper function may have a large total because it calls expensive children. Rewriting the wrapper will not help if the child still performs the same work.
Follow the source location and script URL. A path under wp-content/plugins/ can suggest a plugin owner, while a theme path can suggest template code. Combined bundles require more investigation because several components may share one delivered file.
Use source maps when available. Otherwise, reproduce the issue in staging with asset combination disabled, then compare the relevant requests. Keep other changes out of that experiment so the result remains interpretable.
Also inspect the kind of work inside the task. Repeated layout calculations suggest a different repair from heavy data processing. A script can trigger expensive rendering even when its own function looks short.
Record the URL, task duration, suspected component, and evidence supporting that attribution. Our WordPress performance profiling guide helps connect browser findings with the wider application investigation.
Remove work the page does not need
Suppose a contact-form integration initializes on every article, although only the contact page contains a form. That gives you a specific hypothesis: this template should not load or initialize that feature.
First check whether the plugin offers a supported setting for selective loading. Then inspect how the asset is registered and what depends on it. Avoid editing plugin files directly, because updates can replace those changes.
For custom development, use WordPress’s script enqueueing system and appropriate page conditions. Do not guess a script handle from its filename. Confirm the actual handle and dependencies in the application.
Test the entire feature before keeping an exclusion. A form embedded in a footer or opened through a modal can invalidate a simple page-based rule. A script that looks unused during startup may become necessary after an interaction.
Browser coverage data can help find candidates, but unused during one recording does not mean unnecessary everywhere. Exercise the menu, consent dialog, search, gallery, and other relevant controls before drawing that conclusion.
Apply the same reasoning to third-party tools. A chat widget, experiment platform, or embedded player has an initialization cost. Ask which pages need it and when visitors need its controls.
Our guide to auditing content for performance bloat covers the editorial side of those decisions. Removing an unnecessary embed can eliminate both a request chain and its execution work.
Use defer and async with their actual limits
Loading strategy changes when scripts execute. It does not remove the work inside them.
For classic scripts, defer allows downloading during HTML parsing and preserves deferred-script execution order. async executes when a script becomes available and does not guarantee that order.
That makes dependency handling essential. A dependent component can fail if its library has not executed yet. Our explanation of async and defer covers the loading behavior in more detail.
WordPress supports these strategies through wp_enqueue_script(). It considers the dependency tree when selecting an eligible strategy. The attribute you request is therefore not a substitute for inspecting the generated page.
After changing a strategy, check inline scripts and code that expects globals immediately. Test both uncached and cached loads, because changed request timing can expose ordering problems inconsistently.
Be particularly careful with “delay JavaScript until interaction” features. They may improve a loading report while moving initialization onto the visitor’s first tap. Record that tap and verify that its intended action completes.
Also inspect LCP after changing script behavior. A hero controlled by JavaScript might appear later, even if another score improves. Our LCP diagnosis guide explains how to trace that dependency.
Divide necessary work into smaller tasks
Some work must run, but it does not all need to occupy one uninterrupted task. A custom directory, for example, may prepare hundreds of independent search records during initialization.
Yielding between chunks gives the browser opportunities to process other work. Calling an asynchronous function is insufficient if its body still performs a long synchronous loop. Execution must actually yield.
The following example illustrates a small processing budget between yields. It is a pattern for custom code, not a snippet to paste into an arbitrary plugin.
async function yieldToBrowser() {
if (globalThis.scheduler?.yield) {
await globalThis.scheduler.yield();
} else {
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
async function prepareRecords(records, prepareOne) {
let chunkStarted = performance.now();
for (const record of records) {
prepareOne(record);
if (performance.now() - chunkStarted >= 8) {
await yieldToBrowser();
chunkStarted = performance.now();
}
}
}
Here, prepareOne must be synchronous, independently safe, and quick. The 8 ms budget is an illustrative starting point, not a universal optimum. Measure the resulting trace on the devices you support.
The loop checks its budget after each record. It cannot interrupt a single call that takes 300 ms. That individual operation still needs redesigning or moving elsewhere.
The fallback also does not provide identical scheduling behavior to scheduler.yield(). Feature detection keeps the example usable when the newer API is unavailable. It does not make the two scheduling mechanisms equivalent.
Keep dependent UI state consistent across yields. A visitor might interact between chunks, so partially updated controls need deliberate handling. Cancellation and stale results also need attention when a visitor starts another search.
For optional background work, requestIdleCallback() can be appropriate where supported. Its callback may wait when the browser remains busy. Do not make an essential purchase or navigation action depend on an idle period arriving promptly.
Know when a worker can help
Web Workers can move suitable processing away from the main thread. They cannot directly manipulate the page’s DOM. Moving code that repeatedly measures and updates page elements into a worker therefore requires architectural changes.
A worker may suit a custom computation over a large dataset. Before adopting one, compare processing time with the cost of transferring messages and returning results. Small jobs can gain little from the added coordination.
Keep rendering work on the main thread small when results return. Moving computation away but inserting thousands of elements at once can leave another long task. For a practical introduction, this worked Web Worker example complements the platform documentation.
Use performance.mark() and performance.measure() around your own operations when attribution is difficult. Name the measured operation clearly and remove unnecessary instrumentation afterward. Timings help identify expensive steps; they do not replace a full interaction trace.
Compare a repair with its user-visible result
Imagine an article template initializes an unused slider and an oversized recommendation widget. You remove the slider from that template and divide the widget’s preparation work into smaller chunks.
This illustrative comparison shows the evidence you would collect. It describes a hypothetical experiment, not a measured product result.
| Observation | Before | After | Interpretation |
|---|---|---|---|
| Median loading TBT | 620 ms | 170 ms | Less blocking during the measured load window |
| First menu interaction | 110 ms | 115 ms | No meaningful improvement established by these two samples |
| Recommendation filter interaction | 410 ms | 180 ms | The specific interaction warrants repeated confirmation |
| Checkout behavior | Working | Working | Functional checks still pass in the tested states |
Keep the loading and interaction evidence separate. The menu result prevents you from claiming every interaction improved. The filter result gives you a concrete behavior to retest across devices and data sizes.
Check the states that optimization can break
Before publishing a script-loading change, exercise the affected feature in staging. Use the same test cases again after deployment.
- Test a first visit before consent and another after consent is accepted.
- Test keyboard and touch operation for menus, dialogs, and custom controls.
- Check relevant forms, product variations, add-to-cart actions, and checkout in a safe test flow.
- Compare logged-out and relevant logged-in visits, including warm and cold caches.
- Inspect console errors, missing requests, and the first interaction after a delayed script starts.
- Save the before-and-after traces and monitor subsequent field responsiveness where data is available.
Record the deployment and the assets changed. A WordPress change investigation becomes easier when performance evidence includes a precise time and page scope. If blocking returns, you can compare the new trace with the operation you originally removed.
Leave a Reply