How to Reduce the Impact of Third-Party Code from Google Tag Manager
- Performance
When you run a PageSpeed Insights report, one of the most common recommendations you'll see is:
"Reduce the impact of third-party code."
And if you're using Google Tag Manager (GTM), it's often the first culprit listed.
This can be frustrating โ GTM is essential for analytics, ads, and tag deployment, but it can also slow down your site's Largest Contentful Paint (LCP) and Time to Interactive (TTI) metrics.
The good news is that you can keep GTM and still improve performance by changing when it loads. Instead of running it as soon as the page begins to render, you can delay GTM until after a user interacts with the page โ significantly reducing its impact on your Core Web Vitals.
Why Google Tag Manager Hurts PageSpeed
When GTM loads immediately, it starts fetching and executing all your connected tags before the page is interactive. That means JavaScript execution time goes up, CPU work increases, and your main thread is blocked.
Even if your page looks visually ready, Google's metrics see it as slower because those background tasks delay meaningful paint and interactivity.
Typical Offenders Inside GTM
- Google Analytics or GA4 tags
- Google Ads conversion scripts
- Hotjar, Meta Pixel, and other marketing scripts
- Consent management tools
Each adds milliseconds โ or even seconds โ to your load time.
The Solution: Load GTM After User Interaction
The key insight:
You don't need GTM immediately when the page loads.
If you delay it until a user scrolls, taps, or presses a key, you can serve a near-instant page and still fire GTM before they ever engage meaningfully with content.
This pattern is sometimes called "interaction-triggered loading".
Here's the script we built and use on high-performance sites:
Deferred Google Tag Manager Loader
<!--
Deferred Google Tag Manager loader
Usage:
1) Copy this snippet into your <head> (recommended) or just before </body>.
2) Replace GTM-XXXXXXX in the `gtm.src` URL with your GTM container ID.
3) Adjust triggers below (interaction events, timeout, or window load) to match privacy/consent needs.
Notes:
- This approach queues the dataLayer immediately so other scripts can push to it before GTM loads.
- The loader waits for an early user interaction before fetching gtm.js to avoid blocking render and to improve LCP.
- If you require explicit consent before loading analytics, replace the interaction listeners with your consent callback.
- Default events used: scroll, pointerdown, touchstart, keydown. These are passive and registered once.
- For debugging, temporarily set `loaded = true` to false or remove the `once` option to re-trigger.
-->
<script>
// Ensure a global dataLayer exists immediately so other scripts can push events safely.
window.dataLayer = window.dataLayer || [];
(function () {
// Prevent multiple loads
var loaded = false;
// Configure these values to suit your site / privacy model
var GTM_ID = 'GTM-XXXXXXX'; // <-- Replace with your container ID
var INTERACTION_EVENTS = ['scroll', 'pointerdown', 'touchstart', 'keydown'];
function loadGTM() {
if (loaded) return; // idempotent
loaded = true;
// GTM requires a bootstrap event on dataLayer before gtm.js is executed
dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' });
// Create and append the GTM script
var gtm = document.createElement('script');
gtm.async = true;
gtm.src = 'https://www.googletagmanager.com/gtm.js?id=' + encodeURIComponent(GTM_ID);
document.head.appendChild(gtm);
}
// Register interaction listeners (passive + once) for earliest non-blocking load
var opts = { once: true, passive: true };
INTERACTION_EVENTS.forEach(function (t) {
window.addEventListener(t, loadGTM, opts);
});
// If you need to respect consent, replace the above listeners with your consent handler
// e.g. onConsentGranted(() => loadGTM());
})();
</script> How It Works
- โ Instant page render: The script doesn't block rendering because gtm.js isn't fetched right away.
- โ Preserved dataLayer: We initialize window.dataLayer immediately, so any script pushing events before GTM loads won't break.
- โ First interaction trigger: As soon as the user scrolls, taps, or presses a key, GTM loads asynchronously.
- โ Better LCP & INP scores: By shifting the heavy lifting to post-interaction, your page's core metrics improve โ often by 10โ20 points in PageSpeed Insights.
Privacy & Consent Considerations
If your site requires cookie consent before analytics, this approach integrates naturally: simply replace the interaction listener with your consent callback. For example:
onConsentGranted(() => loadGTM()); This ensures GTM (and all tracking tags) load only after the user has granted permission โ satisfying both performance and compliance requirements.
When Not to Use This Approach
- If you rely on GTM for critical functionality that must run on page load (e.g., A/B testing or essential consent banners).
- If your site runs server-side tagging, which already minimizes the client-side load.
- If your analytics must capture 100% of pageviews (you may lose data for users who bounce before interacting).
For everyone else, this is a smart, simple, and reversible optimization.
Final Thoughts
Modern websites need to balance speed, data, and compliance.
Google Tag Manager is powerful โ but when it loads too early, it hurts the very performance metrics Google uses to rank your site.
This lightweight deferred loader helps you keep GTM flexible without sacrificing performance.