If you’ve tried to track conversions on Shopify’s checkout, you know the pain. Shopify’s checkout runs in a sandboxed environment, which means your standard Google Tag Manager container doesn’t fire there. The solution? Shopify’s Customer Events (Custom Pixels).
In this post, I’m sharing the exact custom pixel code I use for my clients. It tracks all major e-commerce events and pushes them to the data layer in GA4’s expected format - ready for your GTM tags to pick up.
What This Code Tracks
This custom pixel captures the complete customer journey:
| Shopify Event | Data Layer Event | When It Fires |
|---|---|---|
page_viewed | shopify_page_view | Every page load |
product_viewed | view_item | Product page views |
product_added_to_cart | add_to_cart | Add to cart clicks |
cart_viewed | view_cart | Cart page views |
checkout_started | begin_checkout | Checkout initiated |
checkout_shipping_info_submitted | add_shipping_info | Shipping details entered |
payment_info_submitted | add_payment_info | Payment details entered |
checkout_completed | purchase | Order completed |
The Complete Custom Pixel Code
Here’s the full code. Copy it, replace GTM-EXAMPLE with your actual GTM container ID, and paste it into your Shopify Custom Pixel.
// GTM for Shopify Custom Pixel by Adnan Agic
// Contact: agic_adnan@icloud.com
// Replace GTM-EXAMPLE with your GTM container ID
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
(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-EXAMPLE');
// Purchase Event
analytics.subscribe("checkout_completed", (event) => {
dataLayer.push({ ecommerce: null });
const items = event.data?.checkout?.lineItems?.map((item) => {
return {
item_id: item.variant.product.id,
item_name: item.variant.product.title,
price: item.variant.price.amount,
quantity: item.quantity
}
});
dataLayer.push({
event: "purchase",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.checkout?.currencyCode,
value: event.data?.checkout?.subtotalPrice?.amount,
transaction_id: event.data?.checkout?.order?.id,
coupon: event.data?.checkout?.discountAllocations,
shipping: event.data?.checkout?.shippingLine?.price?.amount,
tax: event.data?.checkout?.totalTax?.amount,
items: items
}
});
});
// Payment Info Submitted
analytics.subscribe("payment_info_submitted", (event) => {
dataLayer.push({ ecommerce: null });
const items = event.data?.checkout?.lineItems?.map((item) => {
return {
item_id: item.variant.product.id,
item_name: item.variant.product.title,
price: item.variant.price.amount,
quantity: item.quantity
}
});
dataLayer.push({
event: "add_payment_info",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.checkout?.currencyCode,
value: event.data?.checkout?.subtotalPrice?.amount,
items: items
}
});
});
// Shipping Info Submitted
analytics.subscribe("checkout_shipping_info_submitted", (event) => {
dataLayer.push({ ecommerce: null });
const items = event.data?.checkout?.lineItems?.map((item) => {
return {
item_id: item.variant.product.id,
item_name: item.variant.product.title,
price: item.variant.price.amount,
quantity: item.quantity
}
});
dataLayer.push({
event: "add_shipping_info",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.checkout?.currencyCode,
value: event.data?.checkout?.subtotalPrice?.amount,
items: items
}
});
});
// Checkout Started
analytics.subscribe("checkout_started", (event) => {
dataLayer.push({ ecommerce: null });
const items = event.data?.checkout?.lineItems?.map((item) => {
return {
item_id: item.variant.product.id,
item_name: item.variant.product.title,
price: item.variant.price.amount,
quantity: item.quantity
}
});
dataLayer.push({
event: "begin_checkout",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.checkout?.currencyCode,
value: event.data?.checkout?.subtotalPrice?.amount,
items: items
}
});
});
// Cart Viewed
analytics.subscribe("cart_viewed", (event) => {
dataLayer.push({ ecommerce: null });
const items = event.data?.cart?.lines?.map((item) => {
return {
item_id: item.merchandise.product.id,
item_name: item.merchandise.product.title,
price: item.merchandise.price.amount,
quantity: item.quantity
}
});
dataLayer.push({
event: "view_cart",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.cart?.cost?.totalAmount?.currencyCode,
value: event.data?.cart?.cost?.totalAmount?.amount,
items: items
}
});
});
// Product Added to Cart
analytics.subscribe("product_added_to_cart", (event) => {
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_to_cart",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.cartLine?.cost?.totalAmount?.currencyCode,
value: event.data?.cartLine?.cost?.totalAmount?.amount,
items: [{
item_id: event.data?.cartLine?.merchandise?.product?.id,
item_name: event.data?.cartLine?.merchandise?.product?.title,
price: event.data?.cartLine?.merchandise?.price?.amount,
quantity: event.data?.cartLine?.quantity
}]
}
});
});
// Product Viewed
analytics.subscribe("product_viewed", (event) => {
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_item",
url: event.context.document.location.href,
ecommerce: {
currency: event.data?.productVariant?.price?.currencyCode,
value: event.data?.productVariant?.price?.amount,
items: [{
item_id: event.data?.productVariant?.product?.id,
item_name: event.data?.productVariant?.product?.title,
price: event.data?.productVariant?.price?.amount,
quantity: 1
}]
}
});
});
// Page Viewed
analytics.subscribe("page_viewed", (event) => {
window.dataLayer.push({
event: "shopify_page_view",
url: event.context.document.location.href
});
});
How to Install This on Shopify
- In your Shopify admin, go to Settings → Customer events
- Click Add custom pixel
- Name it something like “GTM Tracking Pixel”
- Paste the code above into the code editor
- Replace
GTM-EXAMPLEwith your actual GTM container ID - Set Permission to “Not required” (or “Required” if you want it to respect consent)
- Click Save, then Connect
Setting Up GTM to Receive These Events
Once the pixel is installed, you need to configure GTM to listen for these events and fire your tags.
Create Data Layer Variables
In GTM, create these Data Layer Variables:
- ecommerce.currency - Variable name:
dlv - ecommerce.currency - ecommerce.value - Variable name:
dlv - ecommerce.value - ecommerce.transaction_id - Variable name:
dlv - ecommerce.transaction_id - ecommerce.items - Variable name:
dlv - ecommerce.items
Create Custom Event Triggers
Create a Custom Event trigger for each event you want to track:
- CE - purchase - Event name:
purchase - CE - add_to_cart - Event name:
add_to_cart - CE - begin_checkout - Event name:
begin_checkout - CE - view_item - Event name:
view_item
Create Your Tags
Now create your Google Ads Conversion or GA4 Event tags, using the data layer variables for dynamic values and the custom event triggers to fire them.
For a detailed walkthrough on the Google Ads side, see my guide on Google Ads conversion tracking for Shopify.
Understanding the Code Structure
Let me break down what’s happening in this code so you can customize it if needed.
The GTM Container Loader
The first block loads your GTM container into the Shopify checkout environment:
(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-EXAMPLE');
This is the standard GTM snippet, modified to work within Shopify’s sandboxed checkout.
The Event Subscribers
Each analytics.subscribe() block listens for a specific Shopify event. When that event fires, the callback function:
- Clears any previous ecommerce data with
dataLayer.push({ ecommerce: null }) - Maps the Shopify event data to GA4’s expected format
- Pushes the formatted data to the data layer
Why Clear Ecommerce Data?
The dataLayer.push({ ecommerce: null }) line before each event is crucial. GA4 and GTM can have issues if ecommerce objects from previous events persist. Clearing it ensures each event has clean, accurate data.
Customization Options
Adding More Item Parameters
You can extend the items array to include more parameters like variant, brand, or category:
const items = event.data?.checkout?.lineItems?.map((item) => {
return {
item_id: item.variant.product.id,
item_name: item.variant.product.title,
item_variant: item.variant.title,
item_brand: item.variant.product.vendor,
price: item.variant.price.amount,
quantity: item.quantity
}
});
Adding User Data for Enhanced Conversions
To enable enhanced conversions, you can add user data to the purchase event:
dataLayer.push({
event: "purchase",
ecommerce: {
// ... existing ecommerce data
},
user_data: {
email: event.data?.checkout?.email,
phone_number: event.data?.checkout?.phone,
address: {
first_name: event.data?.checkout?.billingAddress?.firstName,
last_name: event.data?.checkout?.billingAddress?.lastName,
city: event.data?.checkout?.billingAddress?.city,
region: event.data?.checkout?.billingAddress?.provinceCode,
postal_code: event.data?.checkout?.billingAddress?.zip,
country: event.data?.checkout?.billingAddress?.countryCode
}
}
});
Troubleshooting Common Issues
Events not firing in GTM Preview
GTM Preview mode doesn’t work well with Shopify’s sandboxed checkout. Instead:
- Use your browser’s developer console to check if
dataLayerevents are being pushed - Install the Google Tag Assistant extension
- Make a test purchase and verify conversions appear in Google Ads (with a 1-3 hour delay)
Purchase value showing as 0 or undefined
Check that:
- Your data layer variable path is correct:
ecommerce.value - The Shopify event is returning price data (check console logs)
- You haven’t accidentally nested the ecommerce object incorrectly
Duplicate conversions
If you’re seeing duplicate conversions:
- Make sure you’re using
transaction_idin your Google Ads conversion tag for deduplication - Check you don’t have both this custom pixel AND another conversion tracking method (like Shopify’s native Google integration) running simultaneously
Conclusion
This custom pixel gives you complete control over e-commerce tracking on Shopify. Unlike Shopify’s built-in integrations, you can customize exactly what data is captured, add enhanced conversions, and maintain consistency with your broader GTM setup.
The code is free to use - just replace the container ID and you’re ready to go. If you run into issues or need help with a more complex implementation (like cross-domain tracking or server-side tagging), feel free to reach out.
Related Posts
Why Shopify Sales and Google Ads Sales Don't Match: Understanding the Discrepancy
10 min read
How to Set Up Google Ads Conversion Tracking on Shopify (2026 Guide)
5 min read
How to Connect Shopify to Google Merchant Center
Need Help With Your Google Ads?
I help e-commerce brands scale profitably with data-driven PPC strategies.
Get In Touch