skills/integrations-apis/analytics-integration/SKILL.md
Implement GA4, Meta Pixel, and server-side tagging with a proper data layer so you capture accurate conversion events for ad campaigns
npx skillsauth add finsilabs/awesome-ecommerce-skills analytics-integrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Implement a robust analytics stack for e-commerce using Google Analytics 4 (GA4), Meta Pixel, and Google Tag Manager (GTM). Covers structured data layer design for product and checkout events, server-side tagging via GTM server containers to improve data accuracy and bypass browser restrictions, and Meta Conversions API for reliable ad attribution.
| Platform | Recommended Analytics Setup | Key Actions | |----------|---------------------------|------------| | Shopify | Built-in GA4 integration + GTM app | Connect GA4 in Online Store → Preferences → Google Analytics; install GTM4WP or the official Google & YouTube channel app for Meta Pixel | | WooCommerce | MonsterInsights plugin for GA4 + GTM | Install MonsterInsights (free tier or Pro from $99/yr) for GA4; install WooCommerce Google Analytics Integration (free) for enhanced e-commerce events | | BigCommerce | Native GA4 integration + channel manager | Connect GA4 in Advanced Settings → Data Solutions → Google Analytics; use BigCommerce's Meta Pixel integration under the Channel Manager | | Custom / Headless | GTM container + server-side container + custom data layer | Implement a canonical data layer, deploy GTM server container on Cloud Run or Vercel, and add Meta Conversions API from your backend |
Connect GA4 (built-in, no code required):
page_view, view_item, add_to_cart, begin_checkout, purchasepurchase event as a conversionTrack checkout funnel with GA4 Explorations:
begin_checkout eventadd_shipping_info eventadd_payment_info eventpurchase eventAdd Meta Pixel:
Install MonsterInsights for GA4:
add_to_cart, begin_checkout, and purchase events to GA4 with product-level dataAdd Meta Pixel:
ViewContent, AddToCart, InitiateCheckout, and Purchase eventsVerify events are firing:
Connect GA4 (built-in):
G-XXXXXXXX)purchase with full order dataAdd Meta Pixel:
Design a canonical data layer first — every event follows the same shape regardless of which vendor consumes it:
// dataLayer must be initialized in <head> before GTM loads
window.dataLayer = window.dataLayer || [];
// Always clear ecommerce before pushing a new event (prevents GTM from merging stale items)
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'add_to_cart',
ecommerce: {
currency: 'USD',
value: product.price * quantity,
items: [{
item_id: product.sku,
item_name: product.name,
item_brand: product.brand,
item_category: product.category,
price: product.price,
quantity,
}],
},
});
Purchase event (fire after order confirmed, use server-generated order ID as transaction_id):
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: order.id, // Must be unique — deduplicate browser + server events
value: order.total,
tax: order.tax,
shipping: order.shippingCost,
currency: order.currency,
coupon: order.couponCode || '',
items: order.lineItems.map(line => ({
item_id: line.sku,
item_name: line.name,
price: line.unitPrice,
quantity: line.qty,
})),
},
});
Meta Pixel with Conversions API deduplication — send the same event from browser and server using a shared event_id:
// Browser — pass event_id for deduplication
const eventId = `purchase_${order.id}`;
fbq('track', 'Purchase', {
value: order.total,
currency: order.currency,
content_ids: order.lineItems.map(l => l.sku),
content_type: 'product',
}, { eventID: eventId });
// Send eventId to server for the Conversions API mirror
await fetch('/api/analytics/meta-purchase', {
method: 'POST',
body: JSON.stringify({ orderId: order.id, eventId }),
});
// Server-side Conversions API (Node.js)
import { ServerEvent, EventRequest, UserData, CustomData } from 'facebook-nodejs-business-sdk';
export async function sendMetaPurchase(order, eventId, userAgent, ipAddress) {
const userData = new UserData()
.setEmail(order.customerEmail) // Automatically hashed by SDK
.setClientIpAddress(ipAddress)
.setClientUserAgent(userAgent);
const customData = new CustomData()
.setValue(order.total)
.setCurrency(order.currency)
.setContentIds(order.lineItems.map(l => l.sku));
const serverEvent = new ServerEvent()
.setEventName('Purchase')
.setEventTime(Math.floor(Date.now() / 1000))
.setUserData(userData)
.setCustomData(customData)
.setEventId(eventId) // Matches browser eventID — Meta deduplicates automatically
.setActionSource('website');
await new EventRequest(process.env.META_ACCESS_TOKEN, process.env.META_PIXEL_ID)
.setEvents([serverEvent])
.execute();
}
Initialize Google Consent Mode v2 (required for EU traffic as of March 2024):
// Must run BEFORE GTM or gtag.js loads
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
wait_for_update: 500, // Wait for CMP to update consent
});
// After user accepts cookies via your CMP:
gtag('consent', 'update', {
ad_storage: preferences.marketing ? 'granted' : 'denied',
ad_user_data: preferences.marketing ? 'granted' : 'denied',
analytics_storage: preferences.analytics ? 'granted' : 'denied',
});
Validate events before deploying:
# GA4 Measurement Protocol validation (returns hit validation report)
curl -X POST \
"https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXXXXXX&api_secret=YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"client_id":"test-123","events":[{"name":"purchase","params":{"transaction_id":"T-001","value":59.99,"currency":"USD"}}]}'
ecommerce: null before every e-commerce push — GTM merges data layer objects, so stale item arrays from a previous event will contaminate the next onetransaction_id — never generate it on the client; this ensures deduplication works when both browser and server events firepurchase events behind idempotency checks — store fired transaction_id values in sessionStorage and skip re-firing if the confirmation page is reloaded| Problem | Solution |
|---------|----------|
| Duplicate purchase events in GA4 | Check sessionStorage.getItem('purchase_fired_' + order.id) before pushing; set it after the push fires |
| Items array empty in GTM | Forgot to push { ecommerce: null } before the event — GTM caches the previous items array |
| Meta Pixel and Conversions API both counting conversions | Pass matching eventID (browser) and event_id (server) — Meta deduplicates on this field |
| Shopify GA4 funnel shows no data | Verify the GA4 Measurement ID in Online Store → Preferences matches your property; check GA4 DebugView to confirm events are firing |
| MonsterInsights not tracking WooCommerce orders | Ensure the Pro license is active (Enhanced eCommerce requires Pro); clear any caching plugins after installation |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion