Most Shopify stores we audit have the same tracking setup: scripts pasted directly into theme.liquid, no version control, and no clear picture of what is actually firing on every page load.
Done wrong, GTM adds more JavaScript weight to every page load than the scripts it was meant to replace. Here is the setup that avoids that.
What is Google Tag Manager and why do you need it?
Google Tag Manager is a tag management system that lets you deploy and manage tracking scripts from one place without touching your theme code every time. GA4, Meta Pixel, conversion tags, heatmaps: all managed through one interface.
Without it, every new pixel is added directly to theme.liquid. Your developer gets pulled in for every marketing request. Scripts accumulate with no record of what was added or when. When something breaks, you are searching through raw theme code trying to find which snippet is causing the problem.
GTM puts all of that under control. Version history, a testing environment, and the ability to manage tags without a code deployment.
Step 1: Create your GTM account and container
The initial setup is straightforward, as most Google tools are. First, go to tagmanager.google.com and create an account.
Account setup:
-
Account name: your brand or company name
-
Country: your location
-
Data sharing checkbox: optional, leave it unchecked if you prefer

Container setup:
-
Container name: your domain, for example, yourstore.com
-
Target platform: Web
Click Create, and your container is ready.

On the main Dashboard, your container ID appears at the top of the workspace. It looks like GTM-XXXXXXX.

Before you move on: if you manage multiple stores or have a staging environment, create a separate container for each. Mixing environments in one container means test tags fire on live traffic. We have seen this cause reporting problems that take days to trace back to the source.
Step 2: Add GTM to your Shopify theme
You will then need to add the Google Tag Manager scripts to your website.

Go to Online Store > Themes in your Shopify admin. Next to your active theme, click the three-dot menu and select Edit code. Open layout > theme.liquid.

You need two scripts.
Script 1. As high as possible inside the <head> tag:
html
<!-- Google Tag Manager -->
<script>(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),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->
Script 2. Immediately after the opening <body> tag:
html
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
Replace GTM-XXXXXXX with your container ID in both snippets. Save.
Check this before moving on. If your theme.liquid already has a hardcoded <!-- Google tag (gtag.js) --> block, installed directly or added by an app, you have a conflict. Running a hardcoded GA4 snippet alongside a GA4 tag inside GTM sends duplicate pageview events to your property. Every session gets counted twice.
We recommend removing the hardcoded snippet entirely and letting GTM manage GA4 exclusively. This is one of the first things we check in any tracking audit, and it comes up more often than it should.
Step 3: Configure your first tag and trigger
Click Tags in the left sidebar, then New.
-
Tag name: GA4 - Pageview
-
Tag type: Google Tag (under the Google section)
-
Tag ID: your GA4 Measurement ID, format G-XXXXXXXXXX
Find your Measurement ID in GA4 under Admin > Data Streams > your stream > Measurement ID.

Add one configuration parameter: send_page_view set to true.
Trigger setup:
Under Triggering, create a new trigger:
-
Trigger name: All Pages
-
Trigger type: Page View
-
Fire on: All Page Views
Save the trigger. Save the tag.
“All Pages” is correct for a pageview tag. It is not correct for most other tags. A conversion pixel or heatmap tool firing on every page view runs unnecessary JavaScript on pages where it does nothing.
Step 4: Test in GTM Preview mode before publishing

Click Preview in the top-right of your workspace. Enter your store URL and click Connect. Your store opens in a new tab with Tag Assistant active. You will see "Tag Assistant Connected" at the bottom of the page. The debugger panel shows Tags Fired and Tags Not Fired for each page event.

Your GA4 Pageview tag should appear under Tags Fired on the initial page load. Click through to a product page and confirm it fires there too.
If a tag shows under Tags Not Fired, click it. GTM shows you exactly which trigger condition was not met. That is almost always faster than debugging it on the tag configuration screen. 
One important limitation to know: GTM installed via theme.liquid does not fire on Shopify's checkout or thank-you pages. Checkout tracking requires a separate setup using Shopify's Custom Pixel system, configured under Settings > Customer Events.
If purchase conversion tags are part of your tracking requirements, that is a separate implementation from what this guide covers. Thoroughly test what you have set up in Preview mode on storefront pages, and plan the checkout pixel setup as a follow-on step.
Step 5: Publish your container
Click Submit in the top-right. Name your version before publishing:
-
Good: GA4 pageview tag - initial setup
-
Not useful: Version 2
Click Publish. Your container is live.
Name every publish. When something breaks in three months and you are scanning 15 container versions, the one with a clear name is the one you find immediately.
We recommend treating version names the same way you would treat commit messages in a codebase: specific enough that someone else on the team can understand what changed without opening it.
How GTM affects Shopify site speed, and the fix that actually works
This is the part most setup guides skip.
On a standard installation, GTM fetches the container script and all tag scripts immediately on page load.

On this Shopify store, the GTM tag added 430.9 KiB of network payload for a Lighthouse Performance score of 91.
What is deferred loading?
The standard snippet fires GTM as soon as the browser hits the <head>. Deferred loading is an instruction that delays GTM until the user takes action: scrolls, clicks, moves the mouse, or presses a key.
For most Shopify stores, this is the right tradeoff. Your meaningful tracking events, add to cart, checkout steps, purchase, are all triggered by interaction anyway. A pageview that fires 300 milliseconds after the first scroll does not hurt your analytics. It does help your Lighthouse score and your real-user Core Web Vitals.
How to implement it
In your theme.liquid, replace the standard GTM <head> snippet with this:
html
<!-- Google Tag Manager (deferred load, immediate dataLayer init) -->
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
(function () {
var GTM_ID = 'GTM-XXXXXXX';
var loaded = false;
function loadGTM() {
if (loaded) return;
loaded = true;
var s = document.createElement('script');
s.async = true;
s.src = 'https://www.googletagmanager.com/gtm.js?id=' + GTM_ID;
document.head.appendChild(s);
}
var events = ['scroll', 'mousemove', 'touchstart', 'keydown', 'click'];
events.forEach(function (e) {
window.addEventListener(e, loadGTM, { once: true, passive: true });
});
if ('requestIdleCallback' in window) {
requestIdleCallback(loadGTM, { timeout: 3000 });
} else {
setTimeout(loadGTM, 3000);
}
})();
</script>
<!-- End Google Tag Manager -->
Replace GTM-XXXXXXX with your container ID. The noscript <body> snippet stays unchanged.

What this code does, plainly:
The dataLayer initializes immediately, so no pre-GTM events are lost. GTM itself loads only on the first user interaction. The requestIdleCallback fallback with a 3,000ms timeout means if the user does nothing for three seconds, GTM loads anyway. Bots, crawlers, and quick bounces still get captured.
The passive: true flag on the event listeners is worth paying attention to. It tells the browser these listeners will not block scroll or touch handling. Without it, adding scroll listeners can itself hurt INP, which defeats the purpose of the whole approach.
What this means for INP
Across our speed benchmark of 1,000 Shopify stores, the median INP was 153 milliseconds, within Google's recommended 200ms threshold. Stores running standard GTM with five or more All Pages tags consistently came in higher.
Deferred loading alone will not fix a slow store. It removes a variable that has no business on the main thread before the user scrolls, freeing the browser to handle the initial render without competing tag scripts.

After switching to deferred loading for the same store in the example above, GTM's payload was entirely absent from the initial load. Performance score: 93 (+2 Improvement) Same store, same content, one change.
If your container has grown past ten active tags and INP is consistently above 200 milliseconds, deferred client-side loading has a ceiling.

If INP is already a problem in your store, the Shopify INP Optimization Checklist is a good place to start.
Common GTM mistakes on Shopify
Most GTM problems on Shopify come from the same setup errors, repeated across stores.
-
Duplicate tags. A hardcoded GA4 snippet in your theme, plus a GA4 tag inside GTM, means double-counted sessions. Remove the hardcoded version and let GTM manage it exclusively.
-
Assuming checkout tracking works out of the box. GTM via theme.liquid does not fire on checkout or thank-you pages. Conversion tracking for the full purchase funnel requires Shopify's Custom Pixel system. Plan for it separately.
-
All Pages on everything. Every tag firing on every page view adds JavaScript overhead to pages that have no use for it. Match triggers precisely.
-
Unpublished drafts. GTM does not auto-publish. Agree as a team: changes get tested and published or discarded the same day. Drafts sitting for weeks create confusion about what is actually live on the store.
-
No version names. Ten seconds per publish. Saves hours later.
The container you set up today is the one you will manage for years
GTM is invisible when it works. Tags managed cleanly, marketing independent from developers, and performance where it should be.
The problems start when the container grows without oversight. Tags accumulate. Triggers get duplicated. Nobody tracks what the container is costing you across the site.
The deferred loading approach removes GTM from the parts of your page load that matter most. Combined with trigger discipline as the container grows, it keeps your analytics stack running without becoming a performance drag.
If you want a review of your current GTM container or analytics setup, talk to our team.