All posts
5 min readshopify, web-pixels, ecommerce

Shopify Customer Events and the Web Pixel sandbox: what you can and can't track

Shopify moved custom tracking into Customer Events and a sandboxed Web Pixel API, and it broke a lot of "paste your pixel in theme.liquid" muscle memory. Here's what the sandbox allows and where it bites.


Shopify's Customer Events run your tracking code inside a sandboxed iframe with no access to the storefront DOM, no access to window, and no access to your theme's dataLayer. In exchange you get reliable, structured events for the whole funnel — including checkout, which is otherwise entirely closed to you. Understanding what the sandbox does and does not allow is the difference between a working Shopify implementation and three weeks of fighting the platform.

Why the sandbox exists

Shopify moved checkout to checkout extensibility and replaced arbitrary script injection with Web Pixels. Merchant scripts were a genuine security and performance liability in checkout — a third-party tag with DOM access sits between a customer and their payment details.

So custom pixels now run in a locked-down iframe. They receive a stream of well-defined events from Shopify and can make outbound network calls. They cannot read or modify the page around them.

What you get

Shopify emits a standard set of events, and this is the meaningful upside: they are consistent across themes, and they cover the part of the funnel you could not otherwise instrument.

EventFires when
page_viewedAny storefront page loads
product_viewedA product page loads
product_added_to_cartAn item is added
cart_viewedThe cart is viewed
checkout_startedCheckout begins
checkout_contact_info_submittedContact step completes
checkout_address_info_submittedAddress step completes
payment_info_submittedPayment step completes
checkout_completedThe order is placed
search_submittedA storefront search runs

Each carries structured data — product IDs, variants, prices, quantities, order totals, currency. That is a genuine checkout funnel, which platforms with an open checkout often fail to instrument properly because nobody built the events.

You also receive the customer's consent state, so you can gate sends correctly rather than assuming.

What you cannot do

Be clear about these before designing anything:

  • No DOM access. You cannot read page elements, scrape prices, or attach listeners to buttons.
  • No access to window or your theme's dataLayer. The pixel cannot see what your theme pushed, and your theme cannot see the pixel.
  • No arbitrary third-party script loading in the strict sandbox mode — vendor snippets that expect DOM access will not work as written.
  • No custom events beyond what Shopify emits. If Shopify does not fire it, you do not get it.
  • No shared state with the storefront. Anything the pixel needs must come from the event payload itself.

The practical consequence: your storefront tracking and your pixel tracking are two separate implementations of the same measurement. That duplication is the architecture, not a mistake — but it does mean you must be deliberate about which one owns each event, or you will double-count.

A working custom pixel

Register it under Settings → Customer events → Add custom pixel:

analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;

  fetch('https://your-endpoint.example.com/collect', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      event_name: 'purchase',
      event_id: checkout.order.id,
      value: checkout.totalPrice.amount,
      currency: checkout.currencyCode,
      items: checkout.lineItems.map((li) => ({
        item_id: li.variant.sku,
        item_name: li.title,
        price: li.variant.price.amount,
        quantity: li.quantity
      }))
    })
  });
});

Three things in that snippet matter:

Use the order ID as the event ID. It is stable and unique, which makes deduplication against a server-side event straightforward — the same discipline as Meta's Conversions API.

Send to your own endpoint where you can. A server relay lets you fan out to GA4, Meta, TikTok, and Google Ads from one pixel, and keeps vendor credentials off the client.

Gate on consent. The pixel receives the customer's privacy state; check it before sending anything to an advertising destination. Consent Mode v2 on Shopify covers how consent flows through the platform.

Avoiding duplicate purchases

This is the single most common Shopify tracking bug, and it has an obvious cause: a purchase event fires from the custom pixel and from a legacy theme-level tag, or from Shopify's own Google channel integration at the same time.

Decide explicitly, and write it down:

  • Which single source owns purchase? Usually the custom pixel, because it is the only one that reliably sees checkout completion.
  • Which owns the storefront events? Usually your theme's dataLayer plus GTM, if you run one.
  • Is Shopify's native GA4 integration on or off? It cannot be "sort of on".

Then verify with a real test order: exactly one purchase in GA4, one conversion in Google Ads, one in Meta, and a value matching the Shopify order total.

App pixels versus custom pixels

Apps you install may register their own pixels, which is convenient and opaque. If your numbers look inflated, check the Customer Events list for pixels you did not add — a marketing app that quietly registers a purchase pixel is indistinguishable, in your reports, from a bug you introduced.

FAQ

Can I use Google Tag Manager inside a Shopify custom pixel?

Not usefully. GTM expects DOM and window access that the sandbox does not provide. The workable pattern is a custom pixel that sends events to your own endpoint or a server container, which then fans out to vendors.

Why can my pixel not read my theme's dataLayer?

Because it runs in a sandboxed iframe with no access to the parent page. The pixel and the storefront are separate execution contexts by design — everything the pixel needs must come from the Shopify event payload.

Do I still need theme-level tracking on Shopify?

Often yes, for storefront behaviour a customer-event subscription does not cover — content engagement, scroll, non-commerce interactions. Keep the responsibilities clearly split so purchase is only ever emitted once.

Does the custom pixel respect customer consent?

It receives the consent state and you should gate your sends on it. It does not automatically suppress your outbound requests — that is your code's job.

How do I test a Shopify custom pixel?

Place a real test order — the checkout events cannot be triggered any other way — and watch for the outbound request in the network panel, then confirm the event arrived at the destination with the correct value and order ID.

Check what your storefront is loading outside the sandbox with the free tracking audit — GA4 IDs, duplicate properties, pixels, and consent signals for any URL.


See where your tracking stands

Run the same 13-check audit referenced in this post against any URL. No signup, results in seconds.

Run a free audit