If your conversion tracking still lives in ScriptTags or a theme.liquid snippet, you have a measurement gap right now. The new Shopify checkout never ran that code, so everything from "began checkout" onward is invisible to it. Customer events are Shopify's replacement: one event stream, available on every plan, that works across the storefront and the checkout.
This guide migrates a typical setup in four steps, with code you can paste. (Just need the basics? Adding a custom pixel without an app is the five-minute version, and the event payload reference covers every funnel event in depth.)
How customer events actually work
Shopify publishes a stream of standard events to web pixels: small JavaScript subscribers that run in a sandboxed worker. The stream covers the whole funnel, from page_viewed and product_viewed through product_added_to_cart, checkout_started, and checkout_completed. The full list is in Shopify's standard events reference.
There are two kinds of pixel:
- Custom pixels are pasted into Settings → Customer events → Add custom pixel in the admin. No app, no theme edit. Most merchant tracking belongs here.
- App pixels ship with apps through the Web Pixels API and are present once the app is installed. The Meta, Google, and TikTok channel apps all register one, so check whether your channel app already migrated before writing anything yourself.
The sandbox is the part that surprises people. A pixel cannot touch the DOM, read cookies on your domain directly, or load arbitrary scripts the way a theme snippet could. In exchange, it receives clean, structured payloads and runs on checkout pages where your theme code never could.
Step 1: Inventory what you have
Before writing the new pixel, list every tracker you run today and where it lives:
- Theme code. Search your theme for
gtag(,fbq(,ttq., anddataLayer.push(Online Store → Edit code, then the search box). - ScriptTags installed by apps. The fastest check is the storefront itself: view source and look for
/script_tags/, or filter the network tab by your tracker domains. - The old "Additional scripts" box, if your store is old enough to have used order-status-page scripts.
For each one, write down which events it actually cared about. Almost everything reduces to page views, product views, add to cart, begin checkout, and purchase.
Step 2: Create the custom pixel
Settings → Customer events → Add custom pixel, name it after the destination, and you get a code editor. The entire API surface you need is analytics.subscribe:
analytics.subscribe("page_viewed", (event) => {
console.log("page viewed:", event.context.document.location.href);
});
analytics.subscribe("product_added_to_cart", (event) => {
const line = event.data.cartLine;
console.log("added:", line.merchandise.product.title, line.quantity);
});
analytics.subscribe("checkout_completed", (event) => {
const checkout = event.data.checkout;
console.log("purchase:", checkout.order?.id, checkout.totalPrice.amount);
});
Save, set the pixel's privacy permissions honestly, and click Connect. It's now live on the storefront and the checkout.
Step 3: Wire the payloads to your tracker
The sandbox can load tracker SDKs and can always fetch to a measurement endpoint. Here's GA4, end to end:
const GA_ID = "G-XXXXXXX";
// gtag.js works inside the pixel sandbox: load it, then translate
// Shopify's payloads into GA4 ecommerce events.
const script = document.createElement("script");
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`;
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag("js", new Date());
gtag("config", GA_ID, { send_page_view: false });
analytics.subscribe("page_viewed", (event) => {
gtag("event", "page_view", {
page_location: event.context.document.location.href,
page_title: event.context.document.title,
});
});
analytics.subscribe("product_added_to_cart", (event) => {
const line = event.data.cartLine;
gtag("event", "add_to_cart", {
currency: line.cost.totalAmount.currencyCode,
value: Number(line.cost.totalAmount.amount),
items: [{
item_id: line.merchandise.product.id,
item_name: line.merchandise.product.title,
quantity: line.quantity,
}],
});
});
analytics.subscribe("checkout_completed", (event) => {
const checkout = event.data.checkout;
gtag("event", "purchase", {
transaction_id: checkout.order?.id,
currency: checkout.currencyCode,
value: Number(checkout.totalPrice.amount),
items: checkout.lineItems.map((item) => ({
item_id: item.variant?.product?.id,
item_name: item.title,
quantity: item.quantity,
price: Number(item.variant?.price?.amount ?? 0),
})),
});
});
The document available here belongs to the sandbox, which is why SDK loading works while reading your storefront's DOM doesn't.
One honest warning: don't double-fire. If the official Google or Meta channel app is installed and already sending these events through its app pixel, your custom pixel duplicates every conversion. Pick one path per destination.
Step 4: Verify, then delete the old code
Test with a real order on a development store, or a refunded test order:
- Open Settings → Customer events, confirm the pixel shows as connected, and watch your tracker's debug view (GA4 DebugView, Meta Events Manager test events).
- Confirm
checkout_completedarrives with the right order total. This is the event your old setup was missing entirely. - Only after the new numbers match reality, remove the theme snippets and ask app vendors about leftover ScriptTags. Running both "temporarily" is how stores end up with doubled conversion counts for a year.
Where this intersects with AI agents
Customer events are also how you'll measure the next traffic shift: agentic checkouts. The same checkout_completed payload fires whether a human or an AI shopping assistant completed the purchase. And if you want to see how often AI agents are reading your store before that purchase happens, that's exactly what AgentReady measures from the other side.
Questions about a migration that doesn't fit this shape? Multi-pixel setups, server-side tagging, headless storefronts: get in touch. This is what we do.

Comments
Every comment here comes from a verified email. Write yours, confirm from your inbox, and it's live.
Loading comments…