Google Tag Manager Was Most of My Total Blocking Time. Here Is the Fix I Am Testing
GTM and GA4 caused about 90% of mobile Total Blocking Time on my sites. One page went from 898 ms to 48 ms. How I delay tags safely, and the trade-offs.
In this article
When I ran a performance audit on all eight of my WordPress sites, the server side looked healthy. Pages were cached, images were reasonable, and desktop scores were good. Mobile told a different story, and the cause was not my theme or my plugins. It was analytics.
The measurement
I tested with Lighthouse in a mobile profile: slow 4G network and a CPU slowed down four times, which is closer to a mid-range phone than a developer laptop.
| Metric | Mobile range across sites | Desktop range |
|---|---|---|
| Largest Contentful Paint | 1.2 to 3.8 s | 0.4 to 1.2 s |
| Total Blocking Time | 600 to 1,700 ms | not recorded |
Total Blocking Time (TBT) measures how long the main thread is blocked by long tasks during loading. It is a lab proxy for how responsive a page feels, and it correlates with Interaction to Next Paint (INP), the responsiveness metric in Core Web Vitals.
The JavaScript breakdown pointed at one culprit. Google Tag Manager plus the GA4 tag it loads added up to roughly 330 KB of JavaScript, and accounted for about 90% of the blocking time.
To confirm it, I blocked GTM on one article and measured again:
Total Blocking Time went from 898 ms to 48 ms.
Same page, same server, same content. Just no tag manager during load.
Why not just remove it?
Analytics is how I know which articles work. Removing it would trade a speed problem for a blindness problem. The goal is to keep the data while getting the tags out of the critical loading path.
What is inside the containers
Before changing how GTM loads, I checked what it actually loads. On my sites, each container held only GA4 and Microsoft Clarity. Search Console and Bing Webmaster verification are done through DNS, not through a tag. That matters: if a container also held a verification tag, an ad pixel, or anything that must fire immediately, delaying it could break something.
Check your own container before copying this approach.
The fix: load tags on intent or after a short delay
The approach I am testing replaces the standard GTM snippet with a small loader. GTM starts loading on whichever comes first:
- the first user interaction: scroll, mouse move, touch, or key press
- 3 seconds after the page has loaded
A simplified version looks like this:
<script>
(function () {
var started = false;
function loadGTM() {
if (started) return;
started = true;
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
var f = d.getElementsByTagName(s)[0], j = d.createElement(s);
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i;
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-XXXXXXX');
}
['scroll', 'mousemove', 'touchstart', 'keydown'].forEach(function (e) {
window.addEventListener(e, loadGTM, { once: true, passive: true });
});
window.addEventListener('load', function () { setTimeout(loadGTM, 3000); });
})();
</script>
On WordPress, I did not edit the stored header code. A small must-use plugin filters the output and swaps the standard snippet for the delayed version, and it can be switched on per site. That keeps the change reversible with one setting.
The trade-offs, honestly
Delaying analytics is not free:
- A visitor who lands, reads nothing, and leaves within 3 seconds without touching the screen may not be counted.
- Engagement timing starts slightly later.
- Any tag that must run before the user acts, such as consent tools or conversion pixels on checkout pages, should not be delayed this way.
For content sites with a few analytics tags, those costs are small compared with a page that feels sluggish on every phone. Still, small is not zero, so I am measuring before deciding.
How I am rolling it out
- Started on one site on October 10, 2026.
- Compare analytics for that site against its own recent baseline and against the other sites after a few days. If sessions and engagement stay in line, it is safe.
- Then enable on the other seven, one setting each.
I will update this post with the comparison once the data is in.
If you want to try this
- Run Lighthouse in mobile mode and open the main-thread breakdown. Find out what is actually blocking.
- Block your tag manager in DevTools and measure again. If TBT barely moves, this is not your problem.
- List every tag in your container. Do not delay anything that must fire immediately.
- Ship it on one site or one section first, and compare analytics before rolling it out.
The broader lesson: measure on a phone profile, not on your laptop. My desktop numbers said everything was fine. They were measuring the wrong visitor.