Learn how to implement Meta Pixel (Facebook Pixel) event tracking on Episerver (Optimizely) CMS and Commerce for effective Facebook advertising.
Prerequisites
- Meta Pixel Setup completed
- Meta Pixel base code installed
- Understanding of Meta standard events
Meta Pixel Event Types
Standard Events
Meta provides predefined standard events optimized for Facebook ads:
- PageView - Page view (auto-tracked with base pixel)
- ViewContent - Product/content view
- Search - Search performed
- AddToCart - Product added to cart
- AddToWishlist - Product added to wishlist
- InitiateCheckout - Checkout started
- AddPaymentInfo - Payment info added
- Purchase - Purchase completed
- Lead - Lead form submitted
- CompleteRegistration - Registration completed
- Contact - Contact form submitted
Custom Events
Track custom actions specific to your business needs.
Standard Event Implementation
ViewContent Event
Track when users view products or content.
Product Page Template
@using EPiServer.Commerce.Catalog.ContentTypes
@model ProductPageViewModel
@{
var product = Model.Product;
var variant = Model.SelectedVariant ?? Model.DefaultVariant;
var price = variant?.GetDefaultPrice()?.UnitPrice.Amount ?? 0;
var currency = _currentMarket.GetCurrentMarket().DefaultCurrency.CurrencyCode;
}
<script>
fbq('track', 'ViewContent', {
content_type: 'product',
content_ids: ['@variant?.Code'],
content_name: '@product.DisplayName',
content_category: '@GetCategoryPath(product)',
value: @price,
currency: '@currency'
});
</script>
<!-- Product details markup -->
<div class="product-detail">
<h1>@product.DisplayName</h1>
<p class="price">@price.ToString("C")</p>
<button class="add-to-cart"
data-code="@variant?.Code"
data-name="@product.DisplayName"
data-price="@price"
Add to Cart
</button>
</div>
Content Page Template
@model ArticlePageType
<script>
fbq('track', 'ViewContent', {
content_type: 'article',
content_ids: ['@Model.ContentLink.ID'],
content_name: '@Model.Name',
content_category: '@Model.Category'
});
</script>
Search Event
Track search queries.
@model SearchPageType
@{
var searchTerm = Request.QueryString["q"];
var resultCount = Model.SearchResults?.TotalMatching ?? 0;
}
@if (!string.IsNullOrEmpty(searchTerm))
{
<script>
fbq('track', 'Search', {
search_string: '@searchTerm',
content_category: 'product',
content_ids: [@Html.Raw(string.Join(",", Model.SearchResults?.Select(r => $"'{r.Code}'") ?? new string[0]))],
value: @resultCount
});
</script>
}
AddToCart Event
Track products added to cart.
Client-Side Implementation
// /Scripts/meta-pixel-commerce.js
window.MetaPixelCommerce = window.MetaPixelCommerce || {};
MetaPixelCommerce.trackAddToCart = function(productCode, productName, price, quantity, currency) {
quantity = quantity || 1;
currency = currency || 'USD';
// Call your cart API
fetch('/api/cart/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
code: productCode,
quantity: quantity
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Track add to cart
fbq('track', 'AddToCart', {
content_ids: [productCode],
content_name: productName,
content_type: 'product',
value: price * quantity,
currency: currency
});
// Update cart UI
updateCartCount(data.cartItemCount);
}
})
.catch(error => {
console.error('Error adding to cart:', error);
});
};
Template Usage
<button
'@variant.Code',
'@product.DisplayName',
@price,
1,
'@currency'
);">
Add to Cart
</button>
AddToWishlist Event
Track wishlist additions.
MetaPixelCommerce.trackAddToWishlist = function(productCode, productName, price, currency) {
fbq('track', 'AddToWishlist', {
content_ids: [productCode],
content_name: productName,
content_type: 'product',
value: price,
currency: currency
});
// Call your wishlist API
fetch('/api/wishlist/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: productCode })
});
};
InitiateCheckout Event
Track checkout initiation.
@model CheckoutPageType
@{
var cart = _orderRepository.LoadCart<ICart>(
CustomerContext.Current.CurrentContactId,
"Default");
var contentIds = cart.GetAllLineItems().Select(li => li.Code).ToArray();
var total = cart.GetTotal().Amount;
var currency = cart.Currency.CurrencyCode;
var itemCount = cart.GetAllLineItems().Sum(li => li.Quantity);
}
<script>
fbq('track', 'InitiateCheckout', {
content_ids: [@Html.Raw(string.Join(",", contentIds.Select(c => $"'{c}'")))],
content_type: 'product',
value: @total,
currency: '@currency',
num_items: @itemCount
});
</script>
AddPaymentInfo Event
Track payment information step.
MetaPixelCommerce.trackAddPaymentInfo = function(value, currency, paymentMethod) {
fbq('track', 'AddPaymentInfo', {
content_type: 'product',
value: value,
currency: currency,
payment_method: paymentMethod
});
};
// Call when payment method is selected
document.querySelector('#payment-method').addEventListener('change', function(e) {
var cartTotal = parseFloat(document.querySelector('[data-cart-total]').dataset.cartTotal);
var currency = document.querySelector('[data-currency]').dataset.currency;
MetaPixelCommerce.trackAddPaymentInfo(cartTotal, currency, e.target.value);
});
Purchase Event
Track completed purchases on the order confirmation page.
@model OrderConfirmationPageType
@{
var orderId = RouteData.Values["orderNumber"]?.ToString();
if (!string.IsNullOrEmpty(orderId))
{
var order = _orderRepository.Load<IPurchaseOrder>(orderId);
if (order != null)
{
var contentIds = order.GetAllLineItems().Select(li => li.Code).ToArray();
var total = order.GetTotal().Amount;
var currency = order.Currency.CurrencyCode;
var itemCount = order.GetAllLineItems().Sum(li => li.Quantity);
}
}
}
@if (order != null && HttpContext.Items["PixelPurchaseTracked"] == null)
{
<script>
fbq('track', 'Purchase', {
content_ids: [@Html.Raw(string.Join(",", contentIds.Select(c => $"'{c}'")))],
content_type: 'product',
value: @total,
currency: '@currency',
num_items: @itemCount
});
</script>
@{
HttpContext.Items["PixelPurchaseTracked"] = true;
}
}
Prevent Duplicate Purchase Events
public class PixelPurchaseTrackingFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var orderId = filterContext.RouteData.Values["orderNumber"]?.ToString();
if (!string.IsNullOrEmpty(orderId))
{
var cookieName = $"fb_pixel_tracked_{orderId}";
var cookie = filterContext.HttpContext.Request.Cookies[cookieName];
if (cookie != null)
{
filterContext.HttpContext.Items["PixelPurchaseTracked"] = true;
}
else
{
filterContext.HttpContext.Response.Cookies.Add(new HttpCookie(cookieName, "1")
{
Expires = DateTime.Now.AddDays(7)
});
}
}
}
}
Lead Event
Track lead form submissions.
@model ContactPageType
<form id="contact-form" handleContactSubmit(event)">
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<button type="submit">Submit</button>
</form>
<script>
function handleContactSubmit(event) {
event.preventDefault();
// Track Lead event
fbq('track', 'Lead', {
content_name: 'Contact Form',
content_category: 'contact'
});
// Submit form
event.target.submit();
}
</script>
CompleteRegistration Event
Track user registrations.
@model RegisterPageType
<form id="registration-form" handleRegistration(event)">
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Register</button>
</form>
<script>
function handleRegistration(event) {
event.preventDefault();
var form = event.target;
var formData = new FormData(form);
fetch('/api/account/register', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Track CompleteRegistration
fbq('track', 'CompleteRegistration', {
content_name: 'User Registration',
status: 'completed'
});
// Redirect to success page
window.location.href = data.redirectUrl;
}
});
return false;
}
</script>
Custom Events
Track custom actions specific to your business.
Custom Event Structure
fbq('trackCustom', 'EventName', {
parameter1: 'value1',
parameter2: 'value2'
});
Examples
Newsletter Subscription
function trackNewsletterSignup() {
fbq('trackCustom', 'NewsletterSignup', {
source: 'footer',
category: 'engagement'
});
}
Download Tracking
function trackDownload(fileName, fileType) {
fbq('trackCustom', 'FileDownload', {
file_name: fileName,
file_type: fileType,
content_category: 'resources'
});
}
// Usage
<a href="/documents/whitepaper.pdf" 'PDF')">
Download Whitepaper
</a>
Video Interactions
function trackVideoEvent(action, videoTitle) {
fbq('trackCustom', 'VideoInteraction', {
video_action: action,
video_title: videoTitle
});
}
// Track video play
video.addEventListener('play', function() {
trackVideoEvent('play', 'Product Demo Video');
});
// Track video completion
video.addEventListener('ended', function() {
trackVideoEvent('complete', 'Product Demo Video');
});
Content Engagement
// Track scroll depth
var scrollDepth = [25, 50, 75, 100];
var reached = [];
window.addEventListener('scroll', function() {
var scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
scrollDepth.forEach(function(depth) {
if (scrollPercent >= depth && reached.indexOf(depth) === -1) {
reached.push(depth);
fbq('trackCustom', 'ScrollDepth', {
percent: depth,
page_type: 'article'
});
}
});
});
Episerver-Specific Events
Block Interaction Tracking
@model CTABlockType
<div class="cta-block">
<h3>@Model.Heading</h3>
<button 'BlockInteraction', {
block_type: '@Model.GetOriginalType().Name',
block_id: '@Model.ContentLink.ID',
block_name: '@Model.Name',
interaction_type: 'cta_click'
});">
@Model.ButtonText
</button>
</div>
Episerver Forms Tracking
using EPiServer.Forms.Core.Events;
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class FormPixelEventInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
FormsEvents.Instance.FormSubmitted += OnFormSubmitted;
}
private void OnFormSubmitted(object sender, FormsEventArgs e)
{
var script = $@"
<script>
fbq('track', 'Lead', {{
content_name: '{e.FormsContent.Name}',
content_category: 'form_submission'
}});
</script>
";
HttpContext.Current.Response.Write(script);
}
public void Uninitialize(InitializationEngine context)
{
FormsEvents.Instance.FormSubmitted -= OnFormSubmitted;
}
}
Category Page Views
@model CategoryPageType
<script>
fbq('trackCustom', 'CategoryView', {
category_id: '@Model.ContentLink.ID',
category_name: '@Model.Name',
product_count: @Model.Products.Count()
});
</script>
Product Comparison
MetaPixelCommerce.trackProductComparison = function(productCodes) {
fbq('trackCustom', 'ProductComparison', {
content_ids: productCodes,
num_items: productCodes.length
});
};
// Usage when comparison view loads
MetaPixelCommerce.trackProductComparison(['SKU123', 'SKU456', 'SKU789']);
Advanced Event Parameters
User Data Parameters
Improve event matching with user data:
fbq('track', 'Purchase', {
value: 100.00,
currency: 'USD',
content_ids: ['SKU123']
}, {
// Advanced matching parameters (hashed on client-side)
em: 'hashed_email@example.com',
ph: 'hashed_phone',
external_id: 'user_12345'
});
Server-Side User Data
@{
var contact = CustomerContext.Current.CurrentContact;
var hashedEmail = !string.IsNullOrEmpty(contact?.Email)
? MetaPixelHelper.GetSHA256Hash(contact.Email.ToLower())
: null;
}
<script>
fbq('track', 'Purchase', {
value: @order.GetTotal().Amount,
currency: '@order.Currency.CurrencyCode'
}, {
em: '@hashedEmail',
external_id: '@contact?.PrimaryKeyId'
});
</script>
Event Tracking via GTM
Implement Meta Pixel events through Google Tag Manager.
Step 1: Create Data Layer Event
// Push to data layer
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'addToCart',
'ecommerce': {
'currencyCode': 'USD',
'add': {
'products': [{
'id': 'SKU123',
'name': 'Product Name',
'price': 99.99,
'quantity': 1
}]
}
}
});
Step 2: Create GTM Trigger
- GTM > Triggers > New
- Type: Custom Event
- Event name:
addToCart - Save
Step 3: Create Meta Pixel Tag
- GTM > Tags > New
- Type: Custom HTML
- Code:
<script>
fbq('track', 'AddToCart', {
content_ids: [{{DLV - Product ID}}],
content_name: {{DLV - Product Name}},
value: {{DLV - Product Price}},
currency: {{DLV - Currency}}
});
</script>
- Trigger: addToCart (from step 2)
- Save and publish
Testing Events
1. Meta Pixel Helper
- Install Meta Pixel Helper
- Visit your site
- Perform actions (add to cart, purchase, etc.)
- Check extension for events fired
2. Events Manager Test Events
- Go to Meta Events Manager
- Select your Pixel
- Click "Test Events"
- Enter your website URL
- Perform actions and verify events appear
3. Browser Console
// Check if fbq is available
console.log(typeof fbq);
// Manually fire test event
fbq('track', 'AddToCart', {
content_ids: ['TEST123'],
value: 99.99,
currency: 'USD'
});
4. Network Tab
- Open DevTools Network tab
- Filter for
facebook.com - Perform action
- Check
tr?requests - Verify event parameters in request payload
Event Parameters Reference
Required Parameters
Different events require different parameters:
| Event | Required Parameters |
|---|---|
| ViewContent | content_type, content_ids |
| Search | search_string |
| AddToCart | content_ids, content_type, value, currency |
| AddToWishlist | content_ids, content_type, value, currency |
| InitiateCheckout | content_ids, content_type, value, currency, num_items |
| AddPaymentInfo | value, currency |
| Purchase | value, currency, content_ids |
| Lead | - (no required parameters) |
| CompleteRegistration | status |
Common Optional Parameters
content_name- Product or content namecontent_category- Categorycontent_type- Usually 'product'value- Monetary valuecurrency- ISO currency codenum_items- Number of items
Best Practices
- Use Standard Events: Prefer standard events over custom events when possible
- Include Value: Always include value and currency for commerce events
- Content IDs: Use consistent product SKUs/codes
- Test Thoroughly: Test all events before launching campaigns
- Avoid PII: Don't send personally identifiable information unhashed
- Mobile Compatibility: Ensure events work on mobile devices
- Event Deduplication: Prevent duplicate event firing
- Server-Side Events: Consider Conversions API for critical events
Troubleshooting
Events Not Appearing
Check:
- Meta Pixel Helper shows event fired
- Network tab shows
tr?request - Event parameters are formatted correctly
- No JavaScript errors in console
Duplicate Events
Solution: Implement event deduplication logic
var eventsFired = {};
function trackEventOnce(eventName, params) {
var key = eventName + JSON.stringify(params);
if (!eventsFired[key]) {
fbq('track', eventName, params);
eventsFired[key] = true;
}
}
Events Not Optimizing Campaigns
Check:
- Events are firing correctly
- Sufficient volume (50+ events/week recommended)
- Correct event parameters included
- Events linked to correct ad account
Next Steps
- GTM Implementation - Use GTM for event management
- GA4 Events - Track GA4 events
- Troubleshooting - Fix tracking issues
- Meta Conversions API - Server-side tracking