Shopify · GTM container configuration

Pushing the dataLayer from a Shopify Custom Pixel, the customer-events mapping table, and GTM's correct role on Shopify.

Important limits (read first)#

Running GTM inside the Shopify Custom Pixel (Web Pixel) sandbox is not supported:

  • Since 2025 Shopify removed the ability to paste third-party tags in custom HTML/Scripts; all third-party pixels must use Custom Pixels (sandboxed execution)
  • Google's documentation explicitly does not recommend running GTM or Google tags inside the Custom Pixel sandbox — the sandbox delays and drops events, and Google does not support troubleshooting it
  • The sandbox cannot access the page DOM: scroll depth, file downloads, outbound clicks, affiliate detection and DOM-based form submission are unavailable in a pure Custom Pixel setup
FeatureShopify availability
Ecommerce events (view_item / add_to_cart / begin_checkout / purchase…)Available (Custom Pixel subscribes to customer events and pushes the dataLayer)
Form submission (contact / register / subscribe)Partial (subscribe to form-related customer events)
Outbound clicks / affiliate detectionUnavailable (no DOM access in the sandbox)
Scroll depthUnavailable (no DOM access in the sandbox)
File downloadsUnavailable (no DOM access in the sandbox)

Prerequisites#

  1. Shopify admin access (Settings → Customer events)
  2. A GA4 measurement ID (G-XXXXXXXXXX)
  3. A GTM container (for local preview / QA, or as a future GA4 configuration hop)

Configure a Custom Pixel to push the dataLayer#

  1. Shopify admin → Settings → Customer events
  2. Click Add custom pixel and name it e.g. metricsKit
  3. Paste the code and save
import { register } from '@shopify/web-pixels-extension';

register(async (analytics) => {
  const browser = analytics.browser;

  analytics.subscribeAll((event) => {
    switch (event.name) {
      case 'payment_info_submitted':
        browser.dataLayer.push('event', 'add_payment_info');
        break;
      case 'checkout_address_info_submitted':
        browser.dataLayer.push('event', 'add_shipping_info');
        browser.dataLayer.push('event', 'view_item_list');
        break;
      case 'product_viewed':
        browser.dataLayer.push('event', 'view_item', {
          ecommerce: { items: [toItem(event.data)] }
        });
        break;
      case 'product_added_to_cart':
        browser.dataLayer.push('event', 'add_to_cart', {
          ecommerce: { items: [toItem(event.data)] }
        });
        break;
      case 'checkout_started':
        browser.dataLayer.push('event', 'begin_checkout');
        break;
      case 'checkout_completed':
        browser.dataLayer.push('event', 'purchase', {
          ecommerce: { transaction_id: event.data.checkout.id }
        });
        break;
    }
  });

  function toItem(d) {
    return {
      item_id: d.productVariant.id,
      item_name: d.productVariant.title,
      price: d.productVariant.price.amount,
      currency: d.productVariant.price.currencyCode
    };
  }
});
Note: Custom Pixels are pasted from the Shopify admin only; a third-party GTM container cannot be hosted there. Inside the sandbox use browser.dataLayer, not the page-global dataLayer.

Getting events to GA4#

  • Option A (recommended): Shopify's official Google & YouTube app. Install from the App Store and connect your GA4 property — Shopify's only official path since 2025, with complete events and support
  • Option B: Measurement Protocol (advanced). Fetch the GA4 Measurement Protocol endpoint directly from the Custom Pixel (requires your own api_secret; for teams with development capacity)

On Shopify the GTM container only serves "preview / local QA": temporarily load the container in the theme to validate event parameters; production collection does not depend on GTM.

Note: If you insist on doing configuration in GTM, change browser.dataLayer.push to push to the GTM dataLayer and create Custom Event triggers + GA4 event tags (field values identical to the WordPress guide). Sandbox limits: drops, delays and ordering issues can occur, all unsupported.

GTM container configuration items (Shopify-usable subset)#

If you configure on the GTM side, the container entities match the standard list, but the Shopify sandbox blocks DOM access, so only the ecommerce items apply (the rest are WordPress-only or unavailable):

The complete container configuration is as follows (identical to what the metricsKit "Import file" creates): 2 custom JavaScript variables, 4 data-layer variables, 9 triggers and 10 tags. Every tag is of type "Google Analytics: GA4 Event", using the site's bound GA4 measurement ID.

Note: Prerequisite: the container must already contain a GA4 configuration tag (Google tag / GA4 Configuration with a G-XXXXXXXXXX measurement ID). metricsKit does not create it; without it no event tag will send.
Variable nameTypeConfiguration / Data Layer name
metricsKit - Outbound DomainCustom JavaScriptCompares the clicked link host with the site host to detect outbound clicks
mk - Affiliate DetectorCustom JavaScriptOutputs program / affiliateId / affiliateType from your AFF patterns
dlv - ecommerce.valueData Layer Variableecommerce.value
dlv - ecommerce.currencyData Layer Variableecommerce.currency
dlv - ecommerce.transaction_idData Layer Variableecommerce.transaction_id
dlv - ecommerce.itemsData Layer Variableecommerce.items
Trigger nameTypeConfiguration
metricsKit - Form SubmissionForm SubmissionAll forms
metricsKit - Outbound ClickClick - Just LinksWait for tags 2000ms (timeout 2000ms); validation wait off
metricsKit - Scroll DepthScroll DepthVertical thresholds 25 / 50 / 75 / 100 %
metricsKit - File DownloadLink ClickWait for tags 2000ms; validation wait off
metricsKit - Ecommerce PurchaseCustom EventEvent name purchase
metricsKit - Ecommerce Add To CartCustom EventEvent name add_to_cart
metricsKit - Ecommerce Begin CheckoutCustom EventEvent name begin_checkout
metricsKit - Ecommerce View ItemCustom EventEvent name view_item
metricsKit - Ecommerce RefundCustom EventEvent name refund
Tag nameEvent nameEvent parametersFiring trigger
metricsKit - GA4 Event - Form Submitform_submitformId, formClasses, formText, formUrl, formDestinationForm Submission
metricsKit - GA4 Event - Outbound ClickclicklinkUrl, linkDomain, linkText, outbound=trueOutbound Click
metricsKit - GA4 Event - Scrollscrollpercent_scrolledScroll Depth
metricsKit - GA4 Event - File Downloadfile_downloadfile_name, link_url, link_textFile Download
metricsKit - GA4 Event - Affiliate Clickaffiliate_clickaffiliate=true, affiliate_program, affiliate_id, affiliate_type, link_url, link_textOutbound Click
metricsKit - GA4 Event - Ecommerce Purchasepurchasevalue, currency, transaction_id, items (from the dataLayer ecommerce object)Ecommerce Purchase
metricsKit - GA4 Event - Ecommerce Add To Cartadd_to_cartvalue, currency, transaction_id, itemsEcommerce Add To Cart
metricsKit - GA4 Event - Ecommerce Begin Checkoutbegin_checkoutvalue, currency, transaction_id, itemsEcommerce Begin Checkout
metricsKit - GA4 Event - Ecommerce View Itemview_itemvalue, currency, transaction_id, itemsEcommerce View Item
metricsKit - GA4 Event - Ecommerce Refundrefundvalue, currency, transaction_id, itemsEcommerce Refund
Note: The import file carries the 2 custom JavaScript variables (Outbound Domain, Affiliate Detector); when ecommerce is selected it also contains the 4 dlv - ecommerce.* data-layer variables (ecommerce.value / ecommerce.currency / ecommerce.transaction_id / ecommerce.items). metricsKit never writes to your container — you import every entity in GTM.
Note: Shopify availability: the tags metricsKit - GA4 Event - Ecommerce Purchase / Add To Cart / Begin Checkout / View Item / Refund with their 5 Custom Event triggers and the 2 JS variables are usable (fed by a Custom Pixel pushing the dataLayer). Form submission, outbound click, affiliate, scroll depth and file download are unavailable in the Custom Pixel sandbox (no DOM access) — those entities may be created but will not fire on Shopify; ignore or delete them.

Import file structure example#

Note: The above is a structural example (other entities omitted): the real import file contains all 9 triggers / 10 tags / 6 variables, and its measurement ID (measurementIdOverride), account/container path, site name and the mk - Affiliate Detector script are generated per site from your domain and AFF patterns. Do not copy the sample values — download your own file from Console → Conversions → Download import file.
{
  "containerVersion": {
    "path": "accounts/0000000/containers/0000000/versions/0",
    "name": "metricsKit Tracking (example.com)",
    "trigger": [
      { "triggerId": "100", "name": "metricsKit - Form Submission",
        "type": "FORM_SUBMISSION", "fingerprint": "" },
      { "triggerId": "101", "name": "metricsKit - Outbound Click",
        "type": "LINK_CLICK", "fingerprint": "",
        "waitForTags": { "type": "TEMPLATE", "value": "2000" },
        "waitForTagsTimeout": { "type": "TEMPLATE", "value": "2000" },
        "checkValidation": { "type": "BOOLEAN", "value": "false" } },
      { "triggerId": "104", "name": "metricsKit - Ecommerce Purchase",
        "type": "CUSTOM_EVENT", "fingerprint": "",
        "customEventFilter": [{ "type": "EQUALS", "parameter": [
          { "type": "TEMPLATE", "key": "arg0", "value": "{{_event}}" },
          { "type": "TEMPLATE", "key": "arg1", "value": "purchase" } ] }] }
    ],
    "variable": [
      { "name": "mk - Affiliate Detector", "type": "jsm", "fingerprint": "",
        "parameter": [{ "type": "TEMPLATE", "key": "javascript",
          "value": "function(){ /* generated from your AFF patterns, per-site */ }" }] },
      { "name": "dlv - ecommerce.value", "type": "v", "fingerprint": "",
        "parameter": [{ "type": "TEMPLATE", "key": "name", "value": "ecommerce.value" }] }
    ],
    "tag": [
      { "tagId": "200", "name": "metricsKit - GA4 Event - Ecommerce Purchase",
        "type": "gaawe", "fingerprint": "",
        "parameter": [
          { "type": "TEMPLATE", "key": "measurementIdOverride", "value": "G-XXXXXXXXXX" },
          { "type": "TEMPLATE", "key": "eventName", "value": "purchase" },
          { "type": "TEMPLATE", "key": "value", "value": "{{dlv - ecommerce.value}}" },
          { "type": "TEMPLATE", "key": "items", "value": "{{dlv - ecommerce.items}}" } ],
        "firingTriggerId": ["104"] }
    ]
  }
}

Event mapping quick reference#

Customer event → dataLayer / GA4 event name:

Shopify customer eventdataLayer / GA4 event
payment_info_submittedadd_payment_info
checkout_address_info_submittedadd_shipping_info / view_item_list
product_viewedview_item
product_added_to_cartadd_to_cart
product_removed_from_cartremove_from_cart
cart_viewedview_cart
collection_viewedview_item_list
search_submittedsearch
checkout_startedbegin_checkout
checkout_completedpurchase
checkout_contact_info_submittedlogin
Tip: The customer-event list follows Shopify's official docs (help.shopify.com → Customer events lists all standard events and data fields); when Shopify adds events, extend the mapping in the same pattern.

Verification#

  1. Shopify admin → Settings → Customer events; confirm the pixel is Connected
  2. Place a test order (Shopify Bogus Gateway or another test payment)
  3. The purchase event appears in GA4 Realtime after about 5-30 minutes
  4. Verify data in the metricsKit ecommerce attribution report after 24-48 hours

References#

  • Shopify Customer events: help.shopify.com/en/manual/promoting-marketing/pixels/customer-events
  • Google & YouTube app (official Shopify integration): help.shopify.com/en/manual/promoting-marketing/pixels/google
  • Shopify Web Pixel extension: shopify.dev/docs/api/web-pixels-extension
  • Google's note on why GTM is unsupported in the sandbox: support.google.com/tagmanager/answer/16000892