Shopify Meta Pixel Generator

Meta Pixel as a Shopify custom pixel, with event IDs so Conversions API events don't get double-counted.

Describe the result you need. AI will set up the tool and ask for any missing details.

Configure with AI

Enter to submit · Shift + Enter for a new line

Sent to AI. Leave out passwords and private customer data.

Your details

Events Manager > Data sources > your pixel > Settings.

Lets Meta match this browser event to the same order sent server-side through the Conversions API, so it's only counted once.

Paste into Settings > Customer events > Add custom pixel

meta-custom-pixel.js
// Meta Pixel custom pixel for Shopify (Web Pixels API).
// Paste into Settings > Customer events > Add custom pixel, save, then click Connect.
const PIXEL_ID = '1234567890123456';

// Meta's standard base code, adapted to run inside Shopify's pixel sandbox.
!function(f,b,e,v,n,t,tag){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;
tag=b.getElementsByTagName(e)[0];tag.parentNode.insertBefore(t,tag)}(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', PIXEL_ID);
fbq('track', 'PageView');

const plainId = (id) => (id == null ? undefined : String(id).split('/').pop());
const money = (m) => (m && m.amount != null ? Number(m.amount) : 0);
const round = (x) => Math.round(x * 100) / 100;
function itemOf(variant, quantity) {
  variant = variant || {};
  const product = variant.product || {};
  const variantTitle = variant.untranslatedTitle || variant.title;
  return {
    id: variant.sku || plainId(variant.id),
    name: product.untranslatedTitle || product.title || variant.title,
    variant: variantTitle && variantTitle !== 'Default Title' ? variantTitle : undefined,
    price: money(variant.price),
    quantity: quantity || 1,
  };
}
function checkoutItems(checkout) {
  return (checkout.lineItems || []).map((line) => {
    const quantity = line.quantity || 1;
    const unit = money(line.variant && line.variant.price);
    const allocated = (line.discountAllocations || []).reduce((sum, d) => sum + money(d.amount), 0);
    const price = round(Math.max(0, unit * quantity - allocated) / quantity);
    return Object.assign(itemOf(line.variant, quantity), { price });
  });
}
const itemsValue = (items) => round(items.reduce((sum, item) => sum + item.price * item.quantity, 0));

/* ---------- Events ---------- */
analytics.subscribe('product_viewed', (event) => {
  const variant = event.data.productVariant;
  const item = itemOf(variant, 1);
  fbq('track', 'ViewContent', { content_ids: [item.id], content_type: 'product', content_name: item.name, value: item.price, currency: variant.price.currencyCode }, { eventID: 'view_' + item.id + '_' + Date.now() });
});

analytics.subscribe('product_added_to_cart', (event) => {
  const line = event.data.cartLine;
  if (!line) return;
  const item = itemOf(line.merchandise, line.quantity);
  fbq('track', 'AddToCart', { content_ids: [item.id], content_type: 'product', value: money(line.cost.totalAmount), currency: line.cost.totalAmount.currencyCode }, { eventID: 'cart_' + item.id + '_' + Date.now() });
});

analytics.subscribe('checkout_started', (event) => {
  const checkout = event.data.checkout;
  const items = checkoutItems(checkout);
  fbq('track', 'InitiateCheckout', { content_ids: items.map((i) => i.id), value: itemsValue(items), currency: checkout.currencyCode, num_items: items.reduce((sum, i) => sum + i.quantity, 0) }, { eventID: checkout.token });
});

analytics.subscribe('checkout_completed', (event) => {
  const checkout = event.data.checkout;
  const order = checkout.order || {};
  const items = checkoutItems(checkout);
  const transactionId = plainId(order.id) || checkout.token;
  fbq('track', 'Purchase', { content_ids: items.map((i) => i.id), content_type: 'product', value: itemsValue(items), currency: checkout.currencyCode, num_items: items.reduce((sum, i) => sum + i.quantity, 0) }, { eventID: transactionId });
});

To add it: Shopify admin > Settings > Customer events > Add custom pixel. Name it "Meta Pixel", paste the code, Save, then click Connect. Shopify only runs custom pixels after the shopper accepts Marketing/Analytics where consent is required; check that under Customer privacy > Permission.

Remove the Meta/Facebook Pixel channel under Settings > Apps and sales channels if it's also installed, and any Meta pixel base code pasted into theme.liquid. Two copies double-count every event, including purchases.

The event IDs above only dedupe against a Conversions API event you send server-side with the matching eventID (view_<id>_<timestamp>, cart_<id>_<timestamp>, the checkout token, or the order ID for Purchase). If you don't run Conversions API, dedupe has nothing to match and is harmless to leave on.

What it sends

The pixel loads Meta's standard fbq loader inside Shopify's custom pixel sandbox, then fires ViewContent, AddToCart, InitiateCheckout and Purchase from Shopify's checkout events, mapped to Meta's expected fields (content_ids, value, currency).

Turn on event IDs and each browser event carries an eventID: view_<id>_<timestamp> for views, the cart line ID for adds to cart, the checkout token for InitiateCheckout, and the order ID for Purchase. If you also send the same order through Meta's Conversions API server-side with a matching event ID, Meta counts it once instead of twice.

Installing it

  1. 01

    Get your Pixel ID

    Meta Events Manager > Data sources > your pixel > Settings.

  2. 02

    Generate and paste the code

    Settings > Customer events > Add custom pixel in Shopify admin, then Save and Connect.

  3. 03

    Remove any other Meta pixel

    Check Settings > Apps and sales channels for a Facebook & Instagram channel already sending events, and any Meta pixel base code in theme.liquid. Two live copies double-count Purchase.

Questions people ask

Do I still need the Facebook & Instagram sales channel app?

Not for pixel tracking if you use this custom pixel instead: run one or the other, not both, or Purchase gets counted twice.

What does the event ID actually do without Conversions API?

Nothing by itself: it's a no-op unless a matching server-side event with the same ID also reaches Meta. It's harmless to leave on either way.

Will this track Advantage+ shopping campaigns correctly?

It sends Meta's standard ecommerce events with value and currency, which is what Meta's automated campaign types read. It doesn't set up Advantage+ catalog feeds, which is a separate configuration.

Does consent apply here?

Yes: Shopify only runs custom pixels once the shopper accepts Marketing/Analytics where consent is required, under Customer privacy > Permission in admin.

Want this builtinto your store?

I'm Emmanuel, a Shopify and WooCommerce developer. If this tool pointed at something you'd rather have fixed for you (a faster store, cleaner product data, a migration, a custom feature), send me a note.

Emmanuel Asika

Emmanuel Asika

Shopify & WooCommerce developer, Dublin

I build and fix online stores: new builds, migrations, speed, SEO and the custom bits apps can't do.

hello@emmanuelasika.com