All Shopify users must migrate their Google Tag Manager setup before August 26, 2025 to keep analytics and ad tracking working. This guide explains the steps for setting up Google Tag Manager (GTM) with Shopify’s new custom event tracking system.
Why This Tracking Migration Matters
Shopify is changing how tracking works for store owners. The new model affects anyone using Google Tag Manager (GTM), Google Analytics, or custom event tracking for advertising and performance measurement.
If you rely on tracking for ads, analytics, or conversion measurement, you must update your setup before August 26, 2025. Without the migration, your tracking will break, conversion attribution will be unreliable, and ad platforms will not receive accurate conversion data.
What’s Changing in Shopify Tracking
- Checkout is now on a new extensible model that limits custom script placement
- Events are captured through Shopify’s
analytics.subscribe()API - A custom data layer is needed to push events into GTM
Understanding the New Shopify Tracking Model
Client-Side vs Server-Side Tracking
- Client-Side Tracking: Sends tracking data directly from the visitor’s browser to analytics tools.
- Server-Side Tracking: Sends tracking data from your server to analytics platforms. This offers more reliability and better privacy compliance but requires extra setup.
Impact on GTM Users
You will now need to:
- Subscribe to Shopify standard events
- Push these events to the GTM data layer
- Map these events to your GTM tags
Step-by-Step Guide to Migrating Shopify Tracking
Step 1: Audit Your Current Tracking Setup
- Review your current tracking events
- Check where your GTM container is currently installed
- Identify any scripts added directly into the theme or checkout
Step 2: Prepare Your Google Tag Manager Account
- Confirm you have admin access to GTM
- Export a backup of your current GTM container
- Have your GTM container ID ready (format:
GTM-XXXXXXX)
Step 3: Add the Custom Pixel in Shopify
- Go to your Shopify Admin
- Navigate to Customer events
- Click Add custom pixel
- Name it Google Tag Manager
- Set privacy permissions:
- Under Permission, select No Permission Required
- Under Data sale, choose Data collected does not qualify as data sale
- Paste the GTM tracking code from the next section
- Save and connect the pixel
Insert the GTM Tracking Script
Replace GTM‑YOUR_ID with your Google Tag Manager container ID.
(function () {
// --- 0) Utilities ---
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// --- 1) Load GTM ---
(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‑YOUR_ID');
// Ensure dataLayer exists
window.dataLayer = window.dataLayer || [];
// --- 2) Normalizers (GA4 item format) ---
function fromVariant(variant, qty) {
if (!variant) return null;
const p = variant.product || {};
const priceAmount = (variant.price && (variant.price.amount ?? variant.price)) || 0;
const currency = (variant.price && variant.price.currencyCode) || undefined;
return {
item_id: variant.sku || variant.id || p.id || 'unknown',
item_name: p.title || 'unknown',
item_brand: p.vendor || undefined,
item_category: p.type || undefined,
item_variant: variant.title || undefined,
price: Number(priceAmount),
quantity: Number(qty || 1),
currency: currency
};
}
function fromCartLine(line) {
const v = line?.merchandise;
return fromVariant(v, line?.quantity || 1);
}
function fromCheckoutLine(line) {
const v = line?.variant;
const base = fromVariant(v, line?.quantity || 1);
if (!base) return null;
const amount = line?.finalLinePrice?.amount ?? v?.price?.amount ?? 0;
const code = line?.finalLinePrice?.currencyCode ?? v?.price?.currencyCode;
base.price = Number(amount);
base.currency = code;
return base;
}
// --- 3) Subscriptions (GA4-style events) ---
// page_view
analytics.subscribe('page_viewed', function (e) {
const shop = e?.data?.shop || {};
window.dataLayer.push({
event: 'page_view',
event_id: uuid(),
currency: shop.currency
});
});
// view_item
analytics.subscribe('product_viewed', function (e) {
const v = e?.data?.productVariant;
const item = fromVariant(v, 1);
if (item) {
window.dataLayer.push({
event: 'view_item',
event_id: uuid(),
ecommerce: {
currency: item.currency,
items: [item]
}
});
}
});
// add_to_cart
analytics.subscribe('product_added_to_cart', function (e) {
const line = e?.data?.cartLine;
const item = fromCartLine(line);
if (item) {
window.dataLayer.push({
event: 'add_to_cart',
event_id: uuid(),
ecommerce: {
currency: item.currency,
items: [item]
}
});
}
});
// remove_from_cart
analytics.subscribe('product_removed_from_cart', function (e) {
const line = e?.data?.cartLine;
const item = fromCartLine(line);
if (item) {
window.dataLayer.push({
event: 'remove_from_cart',
event_id: uuid(),
ecommerce: {
currency: item.currency,
items: [item]
}
});
}
});
// view_cart
analytics.subscribe('cart_viewed', function (e) {
const lines = e?.data?.cart?.lines || [];
const items = lines.map(fromCartLine).filter(Boolean);
if (items.length) {
const currency = items.find(i => i.currency)?.currency;
window.dataLayer.push({
event: 'view_cart',
event_id: uuid(),
ecommerce: {
currency: currency,
items: items
}
});
}
});
// begin_checkout
analytics.subscribe('checkout_started', function (e) {
const co = e?.data?.checkout;
const lines = co?.lineItems || [];
const items = lines.map(fromCheckoutLine).filter(Boolean);
window.dataLayer.push({
event: 'begin_checkout',
event_id: uuid(),
ecommerce: {
value: Number(co?.totalPrice?.amount || 0),
currency: co?.currencyCode,
items
}
});
});
// purchase (raw GID transaction_id)
analytics.subscribe('checkout_completed', function (e) {
const co = e?.data?.checkout || {};
const lines = co?.lineItems || [];
const items = lines.map(fromCheckoutLine).filter(Boolean);
const rawId = co?.order?.id || 'unknown'; // keep full Shopify GID
window.dataLayer.push({
event: 'purchase',
event_id: uuid(),
ecommerce: {
transaction_id: rawId, // raw GID preserved
value: Number(co?.totalPrice?.amount || 0),
currency: co?.currencyCode,
items
}
});
});
// view_item_list (collections)
analytics.subscribe('collection_viewed', function (e) {
const col = e?.data?.collection;
const variants = col?.productVariants || [];
const items = variants.map(v => fromVariant(v, 1)).filter(Boolean);
if (items.length) {
window.dataLayer.push({
event: 'view_item_list',
event_id: uuid(),
ecommerce: {
item_list_id: col?.id,
item_list_name: col?.title,
items
}
});
}
});
// search + view_search_results
analytics.subscribe('search_submitted', function (e) {
const sr = e?.data?.searchResult;
const q = sr?.query;
window.dataLayer.push({
event: 'search',
event_id: uuid(),
search_term: q
});
const variants = sr?.productVariants || [];
const items = variants.map(v => fromVariant(v, 1)).filter(Boolean);
if (items.length) {
const currency = items.find(i => i.currency)?.currency;
window.dataLayer.push({
event: 'view_search_results',
event_id: uuid(),
ecommerce: {
currency: currency,
items
}
});
}
});
})();
Step 4: Map Events to GTM Tags
In GTM, create triggers for each event such as begin_checkout, purchase, add_to_cart, and others. Link these triggers to your GA4 events, Google Ads conversions, or Facebook Pixel events.
Step 5: Test and Debug Your Tracking
- Use GTM Preview mode to see events in real time
- Check your analytics platform’s real-time reports
- Confirm events fire with the correct parameters
Step 6: Publish Your Changes
Once you confirm events are tracking correctly, publish your GTM container and continue to monitor in your analytics and ad platforms.
Common Issues and Fixes
GTM container not loading: Check that you replaced GTM‑YOUR_ID with your real container ID.
No events in GTM preview: Verify that the pixel is active under Shopify’s Customer events and that privacy permissions match the recommended settings.
FAQs About Shopify Tracking Migration
1. What happens if I miss the deadline? Your event tracking will break and ad platforms will stop receiving conversion data.
2. Do I need server-side GTM for this? It is optional but provides better tracking accuracy and resilience.
3. Can I still use the Facebook Pixel? Yes, by triggering Facebook Pixel events from GTM using the data layer.
4. How do I check if it works? Test in GTM preview mode and check your analytics platform’s live data.
5. Will my old data be lost? No, existing historical data in analytics tools will remain.
6. Can you set this up for me? Yes. I provide GTM setup, migration, and event tracking services.
Final Thoughts and How I Can Help
Migrating to Shopify’s new tracking model before August 26, 2025 is essential for keeping analytics and advertising performance accurate. With the steps above, you can set up the new system yourself. If you prefer, I can complete the migration for you, ensure events are mapped correctly, and even set up server-side tracking for more reliable data.
📩 Contact me here to get your migration done without the hassle.