Shopify pixel setup: native web pixels and the Meta Pixel, done right

Installing the Meta pixel on Shopify isn't a theme edit anymore: checkout is sandboxed, and the old snippet tutorials leave you with a pixel that goes blind at the one moment that pays. Here's the channel app vs custom pixel call, how to keep purchases from counting twice, and how to prove it fired.

Kay Vink
Kay Vink

If the tutorial you're following says to paste fbevents.js into theme.liquid or drop a purchase snippet in checkout.liquid, it's describing a Shopify that no longer exists, and it will hand you a pixel that goes blind at checkout: the one moment you actually need it. On today's Shopify, installing the Meta pixel means one of two things: the Facebook & Instagram channel app (managed, includes CAPI) or a custom web pixel (sandboxed JavaScript you control). Never a script tag pasted into the theme.

One warning before you start: running the channel app and a manually added pixel double-fires every purchase unless you set up deduplication deliberately. That tension runs through the whole page. The sandbox decides what your pixel can see, and one owner per event decides whether a purchase counts once or twice. What follows is the post-sandbox truth, including the quirks we hit building Buron's own Shopify integration.

#What the web-pixel sandbox changed

Shopify moved all pixels into a sandboxed customer-events system, and checkout stopped being scriptable. The old world (paste fbevents.js into the theme, add purchase snippets to checkout.liquid) is gone: checkout.liquid was retired with Checkout Extensibility, and pixels now run inside a sandbox (an isolated context with a controlled API) rather than with free run of the page. Practical consequences:

  • Pixels subscribe to events; they don't scrape pages. The sandbox emits standard customer events (page_viewed, product_viewed, product_added_to_cart, checkout_started, checkout_completed) and your pixel code subscribes via analytics.subscribe(...).
  • Checkout is reachable only this way. No sandbox, no purchase events. Theme-pasted snippets silently miss the moment that matters most.
  • The sandbox constrains DOM access. Scripts that expect to read arbitrary page state (or write cookies from checkout pages) behave differently inside it. This is the source of most "worked on my old store" surprises.

Everything below is choosing how to get Meta events out of that system.

#Path 1: the Facebook & Instagram channel app

For most stores, the channel app is the right answer: install it and stop. Install from the Shopify App Store, connect your Meta business account and pixel/dataset (The Meta Pixel: what it is, what it sees, where to find your ID covers finding the ID), and it registers an app pixel in the customer-events system. What you get:

  • The standard funnel auto-tracked: page views, product views, add-to-carts, checkout start, purchases, all mapped to Meta standard events.
  • Two transports: browser events through the sandboxed pixel and server-side Conversions API events from Shopify's backend, with dedup handled by the integration.
  • Zero code and platform-maintained mappings as Shopify and Meta evolve.

When it's enough: standard ecommerce funnel, one pixel, no exotic custom events. That's the large majority of stores. When it isn't: custom events beyond the standard set, multi-pixel routing, or parameter enrichment the app doesn't expose. Then:

#Path 2: a custom web pixel

Custom pixels are for events the channel app can't send. Add one under Settings → Customer events → Add custom pixel, as JavaScript running in the sandbox:

analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;
  fbq('track', 'Purchase', {
    value: checkout.totalPrice.amount,
    currency: checkout.totalPrice.currencyCode,
  }, { eventID: event.id }); // the dedup key, see below
});

The subscribe-to-events pattern is the whole model: subscribe, read the event payload, forward. What the sandbox won't give you, the quirks we hit building Buron's Shopify integration rather than read in the docs:

  • The event payload is the API. Fields outside it (arbitrary DOM state, other apps' data) are out of reach by design, especially at checkout.
  • Consent applies. Pixels respect the store's Customer Privacy settings; in consent-gated regions your events start only after opt-in: a real event-count delta, not a bug (Consent Mode v2 without losing your signal).
  • Identifier capture is on you. Click IDs like fbclid arrive on the landing URL and must be captured and forwarded deliberately if your custom events are to match well (Click IDs: what gclid, fbclid, and wbraid do).

#Both at once: the double-fire trap

The channel app already sends Purchase; a custom pixel (or a legacy theme snippet, or a second app) sending Purchase again means every order counts twice: inflated ROAS, mis-trained optimization, and nothing errors. The fix is deduplication by event_id: Meta drops events arriving with the same event ID within the dedup window, whether they came by browser or CAPI.

The operating rules: one owner per standard event. If the channel app sends Purchase, your custom pixel doesn't; custom pixels exist for events the app doesn't send. Where both transports carry the same event deliberately, the shared event_id is mandatory, not optional. And audit for ghosts: stores that migrated from the theme-snippet era often still carry an old pixel: search the theme for fbq( and retire what you find. When the numbers already disagree, Why Facebook Ads conversions don't match Shopify walks the dedup triage.

#Verify it

Five checks, in order, the same pass we run on connected stores:

  1. Place a test order (or run checkout in test mode) on the live store.
  2. Meta Events Manager → your dataset: the funnel events arrive (PageView, ViewContent, AddToCart, InitiateCheckout, Purchase) with sensible value and currency on Purchase.
  3. Dedup check: in the event details, browser and server copies of the same purchase show as deduplicated, not as two events. Two purchases per order is the trap above, live.
  4. Count check over a day or two: Meta-reported purchases ≈ Shopify orders from Meta-attributed traffic. Systematic inflation says double-fire; systematic shortfall says consent, blocking, or a subscription gap.
  5. Re-test after any app install/uninstall or checkout change. Pixel regressions on Shopify almost always trace to one of those. The recurring discipline is The conversion tracking QA checklist: test it like you'd test code.

#Where this stops

This page owns pixel installation on Shopify. The neighboring jobs: server-side tracking and Shopify CAPI beyond the channel app is Server-side tracking for Shopify: the sandbox, CAPI, and the three real routes; the Google Ads side of a Shopify store is Shopify and Google Ads conversion tracking without double counting; and mismatch debugging is Why Facebook Ads conversions don't match Shopify.

The structural advantage you have on Shopify: the platform itself records every order, so your pixel always has a ground truth to be checked against. The catch is that nobody re-runs the five checks above by hand every week. Buron's Shopify integration reads both sides, pixel events and actual orders, and flags when they drift apart, so a pixel regression surfaces as a dated finding in your inbox instead of a quarter of quietly wrong ROAS. [Connect your store →]

Frequently asked questions

How do I add the Meta pixel to Shopify?

Install the Facebook & Instagram channel app and connect your Meta account. It registers the pixel through Shopify's sandboxed web-pixel system and sends both browser and Conversions API events. Don't paste pixel code into your theme: checkout.liquid is gone, and manual snippets can't see checkout. Custom web pixels are for events the app doesn't cover.

Does Shopify have a built-in pixel?

Yes. Shopify's customer events system (web pixels) is built in. App pixels (like the Facebook & Instagram channel's) and custom pixels you add under Settings → Customer events both run in its sandbox, subscribing to standard storefront and checkout events like product_viewed and checkout_completed.