Meta Pixel on Commercetools
This guide covers implementing Meta Pixel (Facebook Pixel) on Commercetools headless commerce for Facebook and Instagram advertising optimization.
Prerequisites
Create Meta Pixel
- Go to Meta Business Suite
- Events Manager → Connect Data Sources → Web → Meta Pixel
- Copy your Pixel ID (15-16 digit number)
Conversions API Access Token (for server-side)
- Events Manager → Settings → Generate Access Token
Client-Side Implementation
Next.js / React
// components/MetaPixel.tsx
'use client';
import Script from 'next/script';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';
declare global {
interface Window {
fbq: (...args: any[]) => void;
_fbq: any;
}
}
interface MetaPixelProps {
pixelId: string;
}
export function MetaPixel({ pixelId }: MetaPixelProps) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
// Track page views on route change
if (window.fbq) {
window.fbq('track', 'PageView');
}
}, [pathname, searchParams]);
return (
<>
<Script id="meta-pixel" strategy="afterInteractive">
{`
!function(f,b,e,v,n,t,s)
{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;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '${pixelId}');
fbq('track', 'PageView');
`}
</Script>
<noscript>
<img
height="1"
width="1"
style={{ display: 'none' }}
src={`https://www.facebook.com/tr?id=${pixelId}&ev=PageView&noscript=1`}
alt=""
/>
</noscript>
</>
);
}
Create Tracking Hook
// hooks/useMetaPixel.ts
export function useMetaPixel() {
const track = (eventName: string, params?: Record<string, any>) => {
if (typeof window !== 'undefined' && window.fbq) {
window.fbq('track', eventName, params);
}
};
const trackCustom = (eventName: string, params?: Record<string, any>) => {
if (typeof window !== 'undefined' && window.fbq) {
window.fbq('trackCustom', eventName, params);
}
};
return { track, trackCustom };
}
E-commerce Event Implementation
// hooks/useMetaEcommerce.ts
import { useMetaPixel } from './useMetaPixel';
import { ProductProjection, Cart, Order } from '@commercetools/platform-sdk';
export function useMetaEcommerce() {
const { track } = useMetaPixel();
const trackViewContent = (product: ProductProjection) => {
const variant = product.masterVariant;
const price = variant.prices?.[0];
track('ViewContent', {
content_ids: [product.id],
content_name: product.name['en-US'],
content_type: 'product',
content_category: product.categories?.[0]?.obj?.name?.['en-US'],
value: price ? price.value.centAmount / 100 : 0,
currency: price?.value.currencyCode || 'USD'
});
};
const trackAddToCart = (product: ProductProjection, quantity: number) => {
const variant = product.masterVariant;
const price = variant.prices?.[0];
track('AddToCart', {
content_ids: [product.id],
content_name: product.name['en-US'],
content_type: 'product',
value: price ? (price.value.centAmount / 100) * quantity : 0,
currency: price?.value.currencyCode || 'USD',
num_items: quantity
});
};
const trackInitiateCheckout = (cart: Cart) => {
track('InitiateCheckout', {
content_ids: cart.lineItems.map(item => item.productId),
content_type: 'product',
value: cart.totalPrice.centAmount / 100,
currency: cart.totalPrice.currencyCode,
num_items: cart.lineItems.reduce((sum, item) => sum + item.quantity, 0)
});
};
const trackAddPaymentInfo = (cart: Cart) => {
track('AddPaymentInfo', {
content_ids: cart.lineItems.map(item => item.productId),
content_type: 'product',
value: cart.totalPrice.centAmount / 100,
currency: cart.totalPrice.currencyCode
});
};
const trackPurchase = (order: Order) => {
track('Purchase', {
content_ids: order.lineItems.map(item => item.productId),
content_type: 'product',
value: order.totalPrice.centAmount / 100,
currency: order.totalPrice.currencyCode,
num_items: order.lineItems.reduce((sum, item) => sum + item.quantity, 0),
contents: order.lineItems.map(item => ({
id: item.productId,
quantity: item.quantity,
item_price: item.price.value.centAmount / 100
}))
});
};
return {
trackViewContent,
trackAddToCart,
trackInitiateCheckout,
trackAddPaymentInfo,
trackPurchase
};
}
Usage in Components
// Product Detail Page
import { useMetaEcommerce } from '@/hooks/useMetaEcommerce';
export function ProductPage({ product }: Props) {
const { trackViewContent } = useMetaEcommerce();
useEffect(() => {
trackViewContent(product);
}, [product]);
return (/* JSX */);
}
// Add to Cart Button
export function AddToCartButton({ product }: Props) {
const { trackAddToCart } = useMetaEcommerce();
const { addToCart } = useCart();
const handleAddToCart = async () => {
await addToCart(product.id, 1);
trackAddToCart(product, 1);
};
return <button onClick={handleAddToCart}>Add to Cart</button>;
}
// Checkout Success
export function CheckoutSuccess({ order }: Props) {
const { trackPurchase } = useMetaEcommerce();
useEffect(() => {
const tracked = sessionStorage.getItem(`meta_purchase_${order.id}`);
if (!tracked) {
trackPurchase(order);
sessionStorage.setItem(`meta_purchase_${order.id}`, 'true');
}
}, [order]);
return (/* JSX */);
}
Server-Side (Conversions API)
For accurate tracking despite ad blockers, implement the Conversions API:
API Route Handler
// app/api/meta-capi/route.ts
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';
const PIXEL_ID = process.env.META_PIXEL_ID!;
const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN!;
function hashData(data: string): string {
return crypto.createHash('sha256').update(data.toLowerCase()).digest('hex');
}
export async function POST(request: NextRequest) {
const body = await request.json();
const { eventName, eventData, userData } = body;
const payload = {
data: [{
event_name: eventName,
event_time: Math.floor(Date.now() / 1000),
action_source: 'website',
event_source_url: eventData.source_url,
user_data: {
em: userData.email ? [hashData(userData.email)] : undefined,
ph: userData.phone ? [hashData(userData.phone)] : undefined,
fn: userData.firstName ? [hashData(userData.firstName)] : undefined,
ln: userData.lastName ? [hashData(userData.lastName)] : undefined,
client_ip_address: request.headers.get('x-forwarded-for')?.split(',')[0],
client_user_agent: request.headers.get('user-agent'),
fbc: eventData.fbc,
fbp: eventData.fbp,
},
custom_data: eventData.customData,
}]
};
const response = await fetch(
`https://graph.facebook.com/v18.0/${PIXEL_ID}/events?access_token=${ACCESS_TOKEN}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
}
);
const result = await response.json();
return NextResponse.json(result);
}
Hybrid Client + Server Tracking
// hooks/useMetaHybrid.ts
import { useMetaPixel } from './useMetaPixel';
export function useMetaHybrid() {
const { track } = useMetaPixel();
const trackPurchase = async (order: Order, userData?: UserData) => {
// Client-side tracking
track('Purchase', {
content_ids: order.lineItems.map(item => item.productId),
value: order.totalPrice.centAmount / 100,
currency: order.totalPrice.currencyCode,
});
// Server-side tracking (Conversions API)
await fetch('/api/meta-capi', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
eventName: 'Purchase',
eventData: {
source_url: window.location.href,
fbc: getCookie('_fbc'),
fbp: getCookie('_fbp'),
customData: {
content_ids: order.lineItems.map(item => item.productId),
value: order.totalPrice.centAmount / 100,
currency: order.totalPrice.currencyCode,
}
},
userData: {
email: userData?.email,
firstName: userData?.firstName,
lastName: userData?.lastName,
}
})
});
};
return { trackPurchase };
}
function getCookie(name: string): string | undefined {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift();
}
Commercetools Subscription for CAPI
// Cloud Function for server-side purchase tracking
import * as functions from '@google-cloud/functions-framework';
import crypto from 'crypto';
const PIXEL_ID = process.env.META_PIXEL_ID!;
const ACCESS_TOKEN = process.env.META_ACCESS_TOKEN!;
functions.cloudEvent('trackMetaPurchase', async (cloudEvent: any) => {
const message = JSON.parse(
Buffer.from(cloudEvent.data.message.data, 'base64').toString()
);
const order = message.order;
const customer = message.customer;
const hashData = (data: string) =>
crypto.createHash('sha256').update(data.toLowerCase()).digest('hex');
const payload = {
data: [{
event_name: 'Purchase',
event_time: Math.floor(Date.now() / 1000),
action_source: 'website',
user_data: {
em: customer?.email ? [hashData(customer.email)] : undefined,
external_id: order.customerId ? [hashData(order.customerId)] : undefined,
},
custom_data: {
content_ids: order.lineItems.map((item: any) => item.productId),
content_type: 'product',
value: order.totalPrice.centAmount / 100,
currency: order.totalPrice.currencyCode,
num_items: order.lineItems.reduce(
(sum: number, item: any) => sum + item.quantity, 0
),
}
}]
};
await fetch(
`https://graph.facebook.com/v18.0/${PIXEL_ID}/events?access_token=${ACCESS_TOKEN}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
}
);
});
Event Reference
| Event | When to Fire | Key Parameters |
|---|---|---|
| PageView | Every page load | (automatic) |
| ViewContent | Product page | content_ids, content_name, value |
| AddToCart | Item added to cart | content_ids, value, currency |
| InitiateCheckout | Checkout starts | content_ids, value, num_items |
| AddPaymentInfo | Payment entered | content_ids, value |
| Purchase | Order complete | content_ids, value, currency, num_items |
Testing and Validation
Meta Pixel Helper
Install Meta Pixel Helper Chrome extension to validate events.
Events Manager Test Events
- Go to Events Manager → Test Events
- Enter your website URL
- Perform actions on your site
- Verify events appear in real-time
Conversions API Diagnostics
Check Events Manager → Data Sources → Your Pixel → Diagnostics for:
- Event match quality
- Deduplication status
- Data freshness
Common Issues
Duplicate Events
When using hybrid (client + server) tracking, add event_id for deduplication:
const eventId = `${order.id}_${Date.now()}`;
// Client-side
fbq('track', 'Purchase', params, { eventID: eventId });
// Server-side
payload.data[0].event_id = eventId;
Missing User Data
Improve match quality by passing hashed user data:
- Email (most important)
- Phone number
- First/Last name
- Country, City
Next Steps
- Troubleshooting - Debug tracking issues
- GA4 Setup - Add Google Analytics
- E-commerce Issues - Revenue tracking guides
Related Resources
- Conversions API Guide - Server-side implementation
- Privacy Compliance - Data handling best practices