{"id":1119,"date":"2026-05-20T15:24:33","date_gmt":"2026-05-20T13:24:33","guid":{"rendered":"https:\/\/www.sabatka.net\/cs\/?p=1119"},"modified":"2026-06-17T20:47:48","modified_gmt":"2026-06-17T18:47:48","slug":"gtm-page-speed-impact","status":"publish","type":"post","link":"https:\/\/www.sabatka.net\/en\/gtm-page-speed-impact\/","title":{"rendered":"GTM and Page Speed: Why Runtime and INP Matter More Than Script Size"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Updated after a LinkedIn discussion. I reworked and expanded this article based on the discussion underneath it. Thanks to everyone who jumped in.<\/p>\n<\/blockquote>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Site speed stopped being a developer concern and became a business one. It shows up directly in revenue: <a href=\"https:\/\/www.akamai.com\/newsroom\/press-release\/akamai-releases-spring-2017-state-of-online-retail-performance-report\" target=\"_blank\" rel=\"noopener\">Akamai data from 10 billion visits<\/a> found that 53 % of mobile users abandon a site that takes over three seconds to load, and <a href=\"https:\/\/web.dev\/case-studies\/milliseconds-make-millions\" target=\"_blank\" rel=\"noopener\">Google and Deloitte measured<\/a> that improving load time by just a tenth of a second lifted retail conversions by 8.4 %.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;Speed&#8221; has two faces. One is how fast the page loads and renders (measured by LCP). The other is how fast the site responds when a user clicks (measured by INP).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GTM is the usual suspect here. You add it, the site slows down, the conclusion suggests itself. Except that conclusion is usually wrong. And occasionally right. Let&#8217;s untangle it.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button scroll_to_subscribe\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">Contact me<\/a><\/div>\n\n\n\n<div class=\"wp-block-button linkedinShare\"><a class=\"wp-block-button__link wp-element-button\">Share on LinkedIN<\/a><\/div>\n<\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">GTM takes the blame. But it only sometimes deserves it.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An empty Google Tag Manager (GTM) container downloads, parses, and runs. The GTM library itself is roughly <a href=\"https:\/\/web.dev\/articles\/tag-best-practices\" target=\"_blank\" rel=\"noopener\">33 KB compressed<\/a> \u2014 less than an average SVG icon.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You could stop here and declare that GTM doesn&#8217;t slow your site. More precisely: the container itself doesn&#8217;t. However:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>What you put in GTM has a huge impact.<\/strong> If you load Google Analytics 4 (GA4) and nothing else, the effect is likely minimal. Add Meta pixel, Google Ads, Sklik, Hotjar, and a pile of other tools, and you&#8217;re looking at a significant slowdown. And the impact of those tools is usually far bigger than the GTM script itself.<\/li>\n\n\n\n<li><strong>The way you use GTM can slow the site down too.<\/strong> With large containers built up over years, I see it regularly. The more hands that touched the container over time, the more likely something inside is eating performance.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">I won&#8217;t dwell on the first point \u2014 deploying a pile of platforms is a business (or marketing) decision you have to accept. But it&#8217;s worth breaking down how to actually use GTM.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Page load \u2014 when size matters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s look at two basic metrics \u2014 how big a script is, and how much time it spends on the browser&#8217;s main thread.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><strong>Script<\/strong><\/th><th><strong>Size (gzip)<\/strong><\/th><th><strong>Main-thread time<\/strong><\/th><\/tr><\/thead><tbody><tr><td>GA4 (gtag.js)<\/td><td>~135 KB<\/td><td>108 ms<\/td><\/tr><tr><td>Meta pixel<\/td><td>~98 KB<\/td><td>514 ms<\/td><\/tr><tr><td>Hotjar<\/td><td>~60 KB<\/td><td>732 ms<\/td><\/tr><tr><td>Microsoft Clarity<\/td><td>~25 KB<\/td><td>405 ms<\/td><\/tr><tr><td>LinkedIn Insight Tag<\/td><td>~10 KB<\/td><td>133 ms<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Size: measured directly from production scripts (gzip transfer), June 2026. Main-thread time: the median time a script spends on the main thread across all sites where it actually runs \u2014 the <a href=\"https:\/\/github.com\/patrickhulce\/third-party-web\" target=\"_blank\" rel=\"noopener\">third-party-web<\/a> dataset (Patrick Hulce), built on <a href=\"https:\/\/httparchive.org\/\" target=\"_blank\" rel=\"noopener\">HTTP Archive<\/a> data from Lighthouse measurements of millions of sites, 2024 snapshot.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Notice the mismatch. GA4 is the largest in the table, yet it loads the main thread the least. Clarity is the smallest, and takes nearly 4\u00d7 as much as GA4. <strong>A script&#8217;s size and its impact on speed are unrelated.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Why size matters so little: GTM and tags load asynchronously and at <a href=\"https:\/\/web.dev\/articles\/fetch-priority\" target=\"_blank\" rel=\"noopener\">low network priority<\/a>. An async script doesn&#8217;t block HTML parsing \u2014 the browser fetches it on the side and keeps building the page, even with the snippet sitting in the head. It&#8217;s not entirely free, though: tags <a href=\"https:\/\/web.dev\/articles\/tag-best-practices\" target=\"_blank\" rel=\"noopener\">compete for bandwidth with critical resources, and once downloaded their code runs on the main thread<\/a>. A lot of tags on a slow device can delay rendering \u2014 but that&#8217;s a second-order effect. The real hit to LCP comes in two cases: when someone drops a synchronous script (without the async attribute) into the head, blocking page parsing, and with A\/B testing tools that use an <a href=\"https:\/\/www.debugbear.com\/blog\/ab-testing-anti-flicker-body-hiding\" target=\"_blank\" rel=\"noopener\">anti-flicker snippet, which deliberately hides the page until the test loads<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What matters is runtime \u2014 how long the code runs on the main thread and blocks it. And size doesn&#8217;t help here: even a one-kilobyte script can freeze the browser with one badly written loop. A large library that spreads its work out over time may not hurt at all.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By the way, I deliberately left GTM out of the table. In this dataset it actually ranks among the worst \u2014 <a href=\"https:\/\/github.com\/patrickhulce\/third-party-web\" target=\"_blank\" rel=\"noopener\">over 1000 ms<\/a> \u2014 but that&#8217;s misleading. GTM is a &#8220;loader&#8221;: its number includes everything you fire through it. That&#8217;s not a property of GTM, it&#8217;s the bill for the tags inside. Which is exactly the point of this article.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One more note: TBT (Total Blocking Time) sums up all main-thread blocking during load \u2014 including the kind broken into small chunks. But fragmented blocking often doesn&#8217;t bother the user at all. What bothers them is a continuous freeze exactly when they want to click. And TBT can&#8217;t see that.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">GTM and how fast your site responds<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">How fast a site responds to user actions is described by <a href=\"https:\/\/web.dev\/articles\/inp\" target=\"_blank\" rel=\"noopener\">INP (Interaction to Next Paint)<\/a>. It measures something simple: you click (or tap, or press a key) and INP measures how long it takes the site to visibly respond. The fewer milliseconds, the snappier the site feels. A response under 200 ms counts as good.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And it&#8217;s not a fringe issue: in 2024 only <a href=\"https:\/\/almanac.httparchive.org\/en\/2024\/performance\" target=\"_blank\" rel=\"noopener\">74 % of mobile sites had good INP versus 97 % on desktop<\/a>. So it&#8217;s mainly a mobile problem \u2014 and that&#8217;s where GTM and tags hurt most.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Why do I talk about INP rather than TBT, which you might know from PageSpeed Insights? Because they measure different things. TBT is calculated in a lab \u2014 Google loads your page on a simulated device and counts blocking during load. INP, on the other hand, is measured on real visitors (Chrome collects it directly). And that&#8217;s what reveals the truth: people often click while the page is still loading, when tags are keeping the browser busy \u2014 and a lab test misses that. <a href=\"http:\/\/web.dev\" target=\"_blank\" rel=\"noopener\">web.dev<\/a> puts it bluntly: <a href=\"https:\/\/web.dev\/articles\/tbt\" target=\"_blank\" rel=\"noopener\">TBT is not a substitute for INP<\/a>. You can have great TBT and still a site that drags under your fingers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That INP isn&#8217;t just numbers for Google is shown by case studies: redBus improved INP by 72 % \u2014 and <a href=\"https:\/\/web.dev\/case-studies\/redbus-inp\" target=\"_blank\" rel=\"noopener\">its sales rose 7 %<\/a>. Economic Times cut main-thread blocking \u2014 and <a href=\"https:\/\/web.dev\/case-studies\/economic-times-inp\" target=\"_blank\" rel=\"noopener\">its bounce rate dropped by half<\/a>.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">How GTM hurts INP<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Loading GTM and tags at the start barely affects INP \u2014 the browser gets that out of the way when the page starts. What hurts response to clicks is something else: tags that install &#8220;watchers&#8221; across the whole page \u2014 on every click, scroll, or mouse move. Auto-event tracking, some heatmaps, and session recorders do this routinely. Such a watcher doesn&#8217;t run once. It fires on every interaction and inserts its code between your click and the site&#8217;s response \u2014 right into the window INP measures. The more such watchers on the page, the lazier every further click. So it&#8217;s not so much about GTM&#8217;s size as about what the specific tags inside do once the site is running.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And what about GTM&#8217;s own built-in triggers \u2014 Click and Form Submission? They watch the whole page too, but do it efficiently: GTM doesn&#8217;t attach a listener to every element, just <a href=\"https:\/\/www.simoahava.com\/analytics\/dont-gtm-listeners-work\/\" target=\"_blank\" rel=\"noopener\">a single one on the document<\/a>. The listener itself is light \u2014 it only reports that a click or form submit happened. What loads INP is what you hang on that event, and one specific trigger: &#8220;Click \u2013 All Elements&#8221; reacts to literally every click anywhere on the page. How to keep the built-in triggers in check, I cover in the next chapter.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">How to keep GTM fast<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are a few techniques for making GTM as fast as possible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Custom JavaScript variables that recompute over and over.<\/strong> Every custom JS variable in GTM is a function the browser evaluates synchronously on the main thread \u2014 again, every time something references it. The variable&#8217;s size doesn&#8217;t matter. Two things hurt. First, chaining: variable A reads variable B, which reads variable C \u2014 and every use of A runs the whole cascade again. Second, computationally heavy operations inside: a regex over long text, parsing a large JSON, walking the entire DOM or a growing dataLayer. On a classic site this gets lost, but in a single-page app the dataLayer grows with every page view \u2014 and the same variables and tags walk it again and again. <a href=\"https:\/\/medium.com\/manomano-tech\/gtm-performance-what-actually-matters-9b8b95827126\" target=\"_blank\" rel=\"noopener\">Real-world measurements<\/a> show how this pushes individual tag execution from tens of milliseconds into the hundreds, even seconds. And by the way \u2014 the number of variables isn&#8217;t the problem; have thousands of them. What matters is what they do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ask developers for clean data.<\/strong> The ideal looks like this: the developer hands clean, complete data into the dataLayer, and in GTM you just read it without computing anything. Note: in practice that often doesn&#8217;t happen and you need to clean, filter, and derive data in GTM. Just do it as little as possible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Measure interactions only after the response renders<\/strong> \u2014 if a developer passes you information about an interaction, defer the <code>dataLayer.push()<\/code> as late as possible (ideally after the response renders) \u2014 <a href=\"https:\/\/www.corewebvitals.io\/pagespeed\/datalayer-inp-yield-pattern\" target=\"_blank\" rel=\"noopener\">it saves tens of milliseconds of INP<\/a>. The logic stays, the runtime work drops.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Templates, not Custom HTML.<\/strong> A Custom HTML tag is a chunk of code GTM injects into the page at runtime \u2014 and the browser has work to do every time it runs. A template does the same thing more cheaply: it&#8217;s prepared up front and GTM runs it in a controlled way. Templates also have <a href=\"https:\/\/developers.google.com\/tag-platform\/tag-manager\/templates\/api\" target=\"_blank\" rel=\"noopener\">template storage<\/a>: you can store the result of a heavier computation and just read the value for the rest of the page&#8217;s life \u2014 so the computation runs only once. If a template exists for your tool (GA4, Meta pixel, Clarity, Hotjar \u2014 they all do), use it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Use simple triggers.<\/strong> Every event push in the dataLayer runs through all active triggers, evaluates their conditions, and resolves the variables those conditions touch. The more events flowing and the heavier the variables in the conditions, the more work on every event. So build trigger conditions on values already in the dataLayer \u2014 not on variables that have to compute something.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Enable built-in click and form listeners deliberately.<\/strong> The &#8220;Click \u2013 All Elements&#8221; trigger captures literally every click on the page, so GTM evaluates something even for clicks you don&#8217;t measure at all. If links are enough for you, use &#8220;Just Links&#8221;; otherwise <a href=\"https:\/\/support.google.com\/tagmanager\/answer\/7679320\" target=\"_blank\" rel=\"noopener\">constrain the trigger with a condition<\/a> (URL or CSS selector) to what you actually track. Google recommends this itself, anyway.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Don&#8217;t fire tags too early.<\/strong> Lots of tags fire on Page View, &#8220;because that&#8217;s how it&#8217;s always been.&#8221; But Page View happens before the page renders. Analytics, heatmap, and remarketing tags have no reason to start that early \u2014 DOM Ready is a sensible compromise. The exception is tags where send speed matters, typically transaction measurement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Cleanup (paused tags, exceptions, unused variables).<\/strong> A paused or deleted tag is excluded from the container. But watch out for blocking via an exception: a tag that &#8220;never fires&#8221; thanks to a trigger exception still stays in the container \u2014 downloaded and compiled for nothing. If you don&#8217;t want a tag, pause or delete it \u2014 don&#8217;t block it with an exception. And unused variables stay in the compiled container too, so clean them out from time to time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By the way \u2014 GTM has concerns beyond speed. On the legal side, the <a href=\"https:\/\/www.sabatka.net\/en\/gtm-v-rozporu-s-gdpr\/\">Hannover court ruling that GTM without consent violates GDPR<\/a> goes into detail. And if you gate tag firing on consent, check the <a href=\"https:\/\/www.sabatka.net\/en\/5-most-common-bugs-in-cookie-bar-configuration\/\">most common mistakes in cookie banner setup<\/a> too.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Is there a problem? And how big?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First, find out whether there&#8217;s anything to fix at all. I built a playground for it at <a href=\"http:\/\/sandbox.sabatka.net\/speed\/start\" target=\"_blank\" rel=\"noopener\">sandbox.sabatka.net\/speed\/start<\/a> \u2014 it compares your configuration against an empty container running just a basic GA4 tag. You&#8217;ll see the relative difference. It&#8217;s not a precise benchmark, but you&#8217;ll immediately know whether the problem is negligible or not.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"http:\/\/sandbox.sabatka.net\/speed\/start\" target=\"_blank\" rel=\"noopener\">GTM speed sandbox<\/a><\/div>\n<\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">For a quick second opinion, use <a href=\"https:\/\/pagespeed.web.dev\/\" target=\"_blank\" rel=\"noopener\">PageSpeed Insights<\/a> \u2014 enter your URL and check whether it flags third-party script issues or poor INP. If not, don&#8217;t bother. Focus on something that actually helps, like content or conversions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you want to measure it yourself, set up realistic conditions:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 1: Copy your production container.<\/strong> Never test in production. Remove or deactivate production IDs (GA4 Measurement ID, Meta pixel ID, etc.) \u2014 otherwise you&#8217;ll send test data to live accounts, and GA4 doesn&#8217;t offer a clean way to filter that out retroactively.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 2: Set up test conditions.<\/strong> Open DevTools (F12) and set:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Network \u2192 <strong>Disable cache<\/strong> (simulates a first visit)<\/li>\n\n\n\n<li>Performance \u2192 <strong>CPU throttling<\/strong> to 4x slowdown (an average Android, not your M3 MacBook)<\/li>\n\n\n\n<li>Network \u2192 <strong>Network throttling<\/strong> to Fast 4G<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Your users on mobile are in exactly these conditions \u2014 or worse.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Warning:<\/strong> The same goes for the sandbox playground \u2014 never enter production IDs (GA4 Measurement ID, Meta pixel ID, etc.). It&#8217;s for orientation, not live data.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">If the difference is small, you probably don&#8217;t need to go further. Clean up the container once in a while \u2014 remove unused tags, clean out orphaned variables, move analytics and remarketing tags from Page View to DOM Ready. That alone often does the job.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1024\" height=\"1024\" data-src=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know.png\" alt=\"\" class=\"wp-image-936 lazyload\" data-srcset=\"https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know.png 1024w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-300x300.png 300w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-150x150.png 150w, https:\/\/www.sabatka.net\/wp-content\/uploads\/2026\/04\/null-did-you-know-768x768.png 768w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/1024;\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<h3 class=\"wp-block-heading\"><strong>Lab note<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Observer_effect_(physics)\" target=\"_blank\" rel=\"noopener\">Observer Effect<\/a> \u2014 a principle from quantum physics where the act of measuring influences what&#8217;s being measured. Every tag you add to measure your site slows down the very site you&#8217;re trying to measure. The more you measure, the slower your site gets.<\/p>\n<\/div>\n<\/div>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Going deeper \u2014 Chrome DevTools<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If every millisecond matters, or you want your container tuned to the max, open Chrome DevTools.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 1: Set up realistic conditions.<\/strong> Same as above \u2014 Disable cache, Network throttling to Fast 4G, CPU throttling to 4x slowdown.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 2: Measure baseline.<\/strong> Test the page with GTM loaded but empty (all tags paused). Record:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/web.dev\/articles\/tbt\" target=\"_blank\" rel=\"noopener\"><strong>TBT<\/strong><\/a> <strong>(Total Blocking Time)<\/strong> \u2014 how much the main thread blocks during load<\/li>\n\n\n\n<li><a href=\"https:\/\/web.dev\/articles\/lcp\" target=\"_blank\" rel=\"noopener\"><strong>LCP<\/strong><\/a> <strong>(Largest Contentful Paint)<\/strong> \u2014 when the largest visible element renders<\/li>\n\n\n\n<li><strong>Waterfall in the Network tab<\/strong> \u2014 load order and duration of scripts<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 3: Measure interaction too, not just load.<\/strong> This is the part most tests skip. After load, perform typical interactions \u2014 click into the menu, open a filter, add to cart \u2014 and in the Performance tab watch how long the main thread runs between the click and the repaint. This is where the global listeners that wreck INP show up. A recording of a real interaction tells you more than any lab number.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Step 4: Add tags incrementally.<\/strong> Activate tags one by one, measure both load and interaction each time, log the delta. It&#8217;s the only way to know which specific script is responsible for what.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At this level, structural changes start to make sense: rewrite Custom HTML tags as templates, lighten heavy variables, break up long main-thread tasks, or move to <a href=\"https:\/\/www.sabatka.net\/en\/co-to-je-server-side-mereni-sgtm\/\">server-side tracking<\/a> which shifts part of the processing from the browser to a server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For common tracking mistakes found in these audits, see my <a href=\"https:\/\/www.sabatka.net\/en\/bugs-in-tracking-measurecamp-czechia-2025\/\">MeasureCamp Czechia presentation<\/a>.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Bottom line<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The GTM container itself doesn&#8217;t slow your site in any dramatic way \u2014 33 KB and done. What actually slows it down is the container&#8217;s contents \u2014 the tags you load through it. The way you use GTM has its share too: how you set up tags, triggers, and variables. And what decides the impact isn&#8217;t script size, as people often claim. It&#8217;s what the code does at runtime, and how fast the site responds to a click.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And one last thing to set straight \u2014 SEO. Core Web Vitals are <a href=\"https:\/\/developers.google.com\/search\/docs\/appearance\/core-web-vitals\" target=\"_blank\" rel=\"noopener\">one signal<\/a> Google weighs, not the dominant factor. Relevant content ranks even on a slower site. Speed won&#8217;t save weak content \u2014 but with otherwise comparable content it tips the balance, and above all it affects your conversions and bounce rate. Which in the end matters more than your position in the results.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The approach stays the same: measure Core Web Vitals before you start optimizing. Fastest via <a href=\"https:\/\/pagespeed.web.dev\/\" target=\"_blank\" rel=\"noopener\">PageSpeed Insights<\/a>, long-term on real users in <a href=\"https:\/\/search.google.com\/search-console\/about\" target=\"_blank\" rel=\"noopener\">Search Console<\/a>; other tools are summarized in <a href=\"https:\/\/web.dev\/articles\/vitals-tools\" target=\"_blank\" rel=\"noopener\">this overview<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Test your site at <a href=\"http:\/\/sandbox.sabatka.net\/speed\/start\" target=\"_blank\" rel=\"noopener\">sandbox.sabatka.net\/speed\/start<\/a>. If you don&#8217;t know what to do with the results, or want help auditing your container \u2014 <a href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">get in touch<\/a>.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button scroll_to_subscribe\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.sabatka.net\/en\/kontakt\/\">Contact me<\/a><\/div>\n\n\n\n<div class=\"wp-block-button linkedinShare\"><a class=\"wp-block-button__link wp-element-button\">Share on LinkedIN<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Updated after a LinkedIn discussion. I reworked and expanded this article based on the discussion underneath it. Thanks to everyone who jumped in. Site speed stopped being a developer concern and became a business one. It shows up directly in revenue: Akamai data from 10 billion visits found that 53 % of mobile users abandon [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1129,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,10],"tags":[],"class_list":["post-1119","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-digital-analytics","category-google-tag-manager-gtm"],"_links":{"self":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/comments?post=1119"}],"version-history":[{"count":14,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1119\/revisions"}],"predecessor-version":[{"id":1163,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/posts\/1119\/revisions\/1163"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/media\/1129"}],"wp:attachment":[{"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/media?parent=1119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/categories?post=1119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sabatka.net\/en\/wp-json\/wp\/v2\/tags?post=1119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}