A well-structured data layer ensures accurate tracking and enables advanced Criteo features. This guide covers data layer architecture, implementation patterns, and best practices for e-commerce tracking.
Data Layer Overview
A data layer is a JavaScript object that stores structured data about the page, products, users, and interactions. It serves as a single source of truth for all tracking tags.
Benefits of Data Layer
- Separation of Concerns - Decouples business logic from tracking code
- Consistency - Single data structure for all marketing tags
- Maintainability - Easier to update tracking without changing tag code
- Reliability - Data available before tags load
- Flexibility - Supports multiple analytics platforms
Data Layer Architecture
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
// Page Information
pageType: 'product', // home, category, product, cart, checkout, confirmation
pagePath: '/products/headphones',
pageTitle: 'Wireless Headphones - Your Store',
// Product Information
product: {
id: 'PROD_123',
name: 'Wireless Headphones',
price: 99.99,
salePrice: 79.99,
currency: 'USD',
brand: 'AudioBrand',
category: 'Electronics/Audio/Headphones',
availability: 'in stock',
imageUrl: 'https://example.com/images/headphones.jpg'
},
// User Information
user: {
id: 'USER_456',
email: 'user@example.com',
isLoggedIn: true,
isNewCustomer: false
},
// Cart Information
cart: {
items: [],
itemCount: 0,
total: 0
},
// Transaction Information
transaction: {
id: '',
revenue: 0,
tax: 0,
shipping: 0,
items: []
}
});
Page-Specific Data Layers
Homepage Data Layer
// Homepage
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'home',
pagePath: '/',
pageTitle: 'Homepage - Your Store',
// Featured products (optional)
featuredProducts: [
{ id: 'PROD_123', name: 'Wireless Headphones', price: 99.99 },
{ id: 'PROD_456', name: 'Bluetooth Speaker', price: 49.99 },
{ id: 'PROD_789', name: 'Smart Watch', price: 199.99 }
],
// User info if logged in
user: {
id: getUserId(),
email: getUserEmail(),
isLoggedIn: isUserLoggedIn()
}
});
Product Page Data Layer
// Product Detail Page
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'product',
pagePath: window.location.pathname,
pageTitle: document.title,
product: {
id: 'PROD_123',
sku: 'WH-1000XM4',
name: 'Wireless Noise-Canceling Headphones',
description: 'Premium wireless headphones with active noise cancellation',
price: 349.99,
salePrice: 299.99,
currency: 'USD',
brand: 'AudioBrand',
category: 'Electronics/Audio/Headphones',
subcategory: 'Over-Ear Headphones',
availability: 'in stock',
stockLevel: 15,
imageUrl: 'https://example.com/images/headphones-main.jpg',
additionalImages: [
'https://example.com/images/headphones-2.jpg',
'https://example.com/images/headphones-3.jpg'
],
// Product attributes
attributes: {
color: 'Black',
size: 'Standard',
material: 'Plastic/Leather'
},
// Variant information
variant: {
id: 'PROD_123_BLACK',
color: 'Black',
sku: 'WH-1000XM4-BLK'
},
// Additional metadata
rating: 4.5,
reviewCount: 1247,
condition: 'new',
gtin: '1234567890123',
mpn: 'WH-1000XM4'
},
user: {
id: getUserId(),
email: getUserEmail(),
isLoggedIn: isUserLoggedIn()
}
});
Category Page Data Layer
// Category/Listing Page
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'category',
pagePath: '/category/headphones',
pageTitle: 'Headphones - Your Store',
category: {
id: 'CAT_HEADPHONES',
name: 'Headphones',
path: 'Electronics/Audio/Headphones',
productCount: 45
},
// Visible products on page
products: [
{
id: 'PROD_123',
name: 'Wireless Headphones',
price: 99.99,
brand: 'AudioBrand',
position: 1
},
{
id: 'PROD_456',
name: 'Gaming Headset',
price: 79.99,
brand: 'GameAudio',
position: 2
},
{
id: 'PROD_789',
name: 'Studio Monitor Headphones',
price: 149.99,
brand: 'ProAudio',
position: 3
}
],
// Filters applied
filters: {
priceRange: '50-150',
brand: ['AudioBrand', 'GameAudio'],
features: ['wireless', 'noise-canceling']
},
user: {
id: getUserId(),
email: getUserEmail(),
isLoggedIn: isUserLoggedIn()
}
});
Shopping Cart Data Layer
// Cart Page
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'cart',
pagePath: '/cart',
pageTitle: 'Shopping Cart - Your Store',
cart: {
items: [
{
id: 'PROD_123',
name: 'Wireless Headphones',
sku: 'WH-1000XM4',
price: 99.99,
quantity: 1,
brand: 'AudioBrand',
category: 'Electronics/Audio/Headphones',
variant: 'Black',
position: 0
},
{
id: 'PROD_456',
name: 'Bluetooth Speaker',
sku: 'SPK-200',
price: 49.99,
quantity: 2,
brand: 'AudioBrand',
category: 'Electronics/Audio/Speakers',
variant: 'Blue',
position: 1
}
],
itemCount: 3,
subtotal: 199.97,
tax: 0,
shipping: 0,
discount: 20.00,
couponCode: 'SAVE20',
total: 179.97,
currency: 'USD'
},
user: {
id: getUserId(),
email: getUserEmail(),
isLoggedIn: isUserLoggedIn()
}
});
Checkout Page Data Layer
// Checkout Page
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'checkout',
pagePath: '/checkout',
pageTitle: 'Checkout - Your Store',
checkoutStep: 1, // 1=shipping, 2=payment, 3=review
cart: {
items: [
{
id: 'PROD_123',
name: 'Wireless Headphones',
price: 99.99,
quantity: 1
},
{
id: 'PROD_456',
name: 'Bluetooth Speaker',
price: 49.99,
quantity: 2
}
],
subtotal: 199.97,
tax: 16.00,
shipping: 10.00,
discount: 20.00,
total: 205.97,
currency: 'USD'
},
user: {
id: getUserId(),
email: getUserEmail(),
isLoggedIn: isUserLoggedIn(),
shippingAddress: {
country: 'US',
state: 'CA',
city: 'San Francisco',
zipCode: '94102'
}
}
});
Order Confirmation Data Layer
// Order Confirmation Page
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: 'confirmation',
pagePath: '/order/confirmation',
pageTitle: 'Order Confirmation - Your Store',
transaction: {
id: 'ORDER_12345',
affiliation: 'Online Store',
revenue: 205.97,
tax: 16.00,
shipping: 10.00,
discount: 20.00,
coupon: 'SAVE20',
currency: 'USD',
items: [
{
id: 'PROD_123',
name: 'Wireless Headphones',
sku: 'WH-1000XM4',
price: 99.99,
quantity: 1,
brand: 'AudioBrand',
category: 'Electronics/Audio/Headphones',
variant: 'Black'
},
{
id: 'PROD_456',
name: 'Bluetooth Speaker',
sku: 'SPK-200',
price: 49.99,
quantity: 2,
brand: 'AudioBrand',
category: 'Electronics/Audio/Speakers',
variant: 'Blue'
}
]
},
user: {
id: 'USER_456',
email: 'user@example.com',
isLoggedIn: true,
isNewCustomer: true // First purchase
}
});
Mapping Data Layer to Criteo Events
Homepage Mapping
// Read from data layer
const pageData = window.dataLayer[0];
// Map to Criteo event
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType }
);
if (pageData.user.email) {
window.criteo_q.push({
event: "setEmail",
email: pageData.user.email
});
}
if (pageData.pageType === 'home') {
window.criteo_q.push({ event: "viewHome" });
}
Product Page Mapping
const pageData = window.dataLayer[0];
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType }
);
if (pageData.user.email) {
window.criteo_q.push({ event: "setEmail", email: pageData.user.email });
}
if (pageData.pageType === 'product' && pageData.product) {
window.criteo_q.push({
event: "viewItem",
item: pageData.product.id
});
}
Cart Page Mapping
const pageData = window.dataLayer[0];
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType }
);
if (pageData.user.email) {
window.criteo_q.push({ event: "setEmail", email: pageData.user.email });
}
if (pageData.pageType === 'cart' && pageData.cart.items.length > 0) {
const cartItems = pageData.cart.items.map(item => ({
id: item.id,
price: item.price,
quantity: item.quantity
}));
window.criteo_q.push({
event: "viewBasket",
item: cartItems
});
}
Transaction Mapping
const pageData = window.dataLayer[0];
window.criteo_q = window.criteo_q || [];
window.criteo_q.push(
{ event: "setAccount", account: 12345 },
{ event: "setSiteType", type: deviceType }
);
if (pageData.user.email) {
window.criteo_q.push({ event: "setEmail", email: pageData.user.email });
}
if (pageData.pageType === 'confirmation' && pageData.transaction) {
const transactionItems = pageData.transaction.items.map(item => ({
id: item.id,
price: item.price,
quantity: item.quantity
}));
window.criteo_q.push({
event: "trackTransaction",
id: pageData.transaction.id,
item: transactionItems,
new_customer: pageData.user.isNewCustomer ? 1 : 0
});
}
Generic Data Layer to Criteo Mapper
Create a reusable function to map any data layer to Criteo:
// criteo-mapper.js
function mapDataLayerToCriteo() {
// Get data layer
const pageData = window.dataLayer?.[0];
if (!pageData) {
console.warn('Data layer not found');
return;
}
// Initialize Criteo queue
window.criteo_q = window.criteo_q || [];
// Device detection
const deviceType = /iPad/.test(navigator.userAgent) ? "t" :
/Mobile|iP(hone|od)|Android|BlackBerry|IEMobile|Silk/.test(navigator.userAgent) ? "m" : "d";
// Core events (required on all pages)
window.criteo_q.push(
{ event: "setAccount", account: window.CRITEO_ACCOUNT_ID },
{ event: "setSiteType", type: deviceType }
);
// User email (if available)
if (pageData.user?.email) {
window.criteo_q.push({
event: "setEmail",
email: pageData.user.email
});
}
// Page-specific events
switch (pageData.pageType) {
case 'home':
window.criteo_q.push({ event: "viewHome" });
break;
case 'product':
if (pageData.product?.id) {
window.criteo_q.push({
event: "viewItem",
item: pageData.product.id
});
}
break;
case 'category':
if (pageData.products?.length > 0) {
const productIds = pageData.products.slice(0, 3).map(p => p.id);
window.criteo_q.push({
event: "viewList",
item: productIds
});
}
break;
case 'cart':
if (pageData.cart?.items?.length > 0) {
const cartItems = pageData.cart.items.map(item => ({
id: item.id,
price: item.price,
quantity: item.quantity
}));
window.criteo_q.push({
event: "viewBasket",
item: cartItems
});
}
break;
case 'confirmation':
if (pageData.transaction?.id) {
const transactionItems = pageData.transaction.items.map(item => ({
id: item.id,
price: item.price,
quantity: item.quantity
}));
window.criteo_q.push({
event: "trackTransaction",
id: pageData.transaction.id,
item: transactionItems,
new_customer: pageData.user?.isNewCustomer ? 1 : 0
});
}
break;
default:
console.warn('Unknown page type:', pageData.pageType);
}
}
// Execute after data layer is ready
if (document.readyState === 'complete') {
mapDataLayerToCriteo();
} else {
window.addEventListener('load', mapDataLayerToCriteo);
}
E-commerce Platform Integration
Shopify Data Layer
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: '{{ template.name }}',
{% if template.name == 'product' %}
product: {
id: '{{ product.id }}',
name: '{{ product.title | escape }}',
price: {{ product.price | money_without_currency }},
{% if product.compare_at_price > product.price %}
salePrice: {{ product.price | money_without_currency }},
originalPrice: {{ product.compare_at_price | money_without_currency }},
{% endif %}
brand: '{{ product.vendor | escape }}',
category: '{{ product.type | escape }}',
availability: '{% if product.available %}in stock{% else %}out of stock{% endif %}',
imageUrl: 'https:{{ product.featured_image | img_url: "large" }}'
},
{% endif %}
{% if template.name == 'cart' %}
cart: {
items: [
{% for item in cart.items %}
{
id: '{{ item.product_id }}',
name: '{{ item.title | escape }}',
price: {{ item.final_price | money_without_currency }},
quantity: {{ item.quantity }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
],
total: {{ cart.total_price | money_without_currency }}
},
{% endif %}
{% if customer %}
user: {
id: '{{ customer.id }}',
email: '{{ customer.email }}',
isLoggedIn: true
}
{% endif %}
});
</script>
WooCommerce Data Layer
<!-- In functions.php or custom plugin -->
<?php
function add_criteo_data_layer() {
?>
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageType: '<?php echo get_page_type(); ?>',
<?php if (is_product()) : ?>
<?php global $product; ?>
product: {
id: '<?php echo $product->get_id(); ?>',
name: '<?php echo esc_js($product->get_name()); ?>',
price: <?php echo $product->get_price(); ?>,
brand: '<?php echo esc_js(get_product_brand($product)); ?>',
category: '<?php echo esc_js(get_product_category($product)); ?>',
availability: '<?php echo $product->is_in_stock() ? "in stock" : "out of stock"; ?>'
},
<?php endif; ?>
<?php if (is_cart()) : ?>
cart: {
items: [
<?php
foreach (WC()->cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
echo '{';
echo 'id: "' . $product->get_id() . '",';
echo 'name: "' . esc_js($product->get_name()) . '",';
echo 'price: ' . $product->get_price() . ',';
echo 'quantity: ' . $cart_item['quantity'];
echo '},';
}
?>
],
total: <?php echo WC()->cart->get_total(''); ?>
},
<?php endif; ?>
<?php if (is_user_logged_in()) : ?>
<?php $user = wp_get_current_user(); ?>
user: {
id: '<?php echo $user->ID; ?>',
email: '<?php echo $user->user_email; ?>',
isLoggedIn: true
}
<?php endif; ?>
});
</script>
<?php
}
add_action('wp_head', 'add_criteo_data_layer');
?>
Magento Data Layer
<!-- In template file -->
<?php
$dataLayer = [
'pageType' => $this->getPageType(),
];
if ($this->getPageType() === 'product') {
$product = $this->getProduct();
$dataLayer['product'] = [
'id' => $product->getId(),
'name' => $product->getName(),
'price' => $product->getFinalPrice(),
'brand' => $product->getAttributeText('brand'),
'category' => $this->getCategoryPath($product),
'availability' => $product->isAvailable() ? 'in stock' : 'out of stock'
];
}
?>
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push(<?php echo json_encode($dataLayer); ?>);
</script>
Dynamic Data Layer Updates
For single-page applications or AJAX interactions:
// Add item to cart (AJAX)
function addToCartAndUpdateDataLayer(productId, quantity) {
// Add to cart via API
fetch('/api/cart/add', {
method: 'POST',
body: JSON.stringify({ productId, quantity })
})
.then(response => response.json())
.then(cart => {
// Update data layer
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'addToCart',
cart: {
items: cart.items,
total: cart.total
}
});
// Fire Criteo event
window.criteo_q = window.criteo_q || [];
window.criteo_q.push({
event: "addToCart",
product: {
id: productId,
quantity: quantity
}
});
});
}
// Remove item from cart
function removeFromCartAndUpdateDataLayer(productId) {
fetch('/api/cart/remove', {
method: 'POST',
body: JSON.stringify({ productId })
})
.then(response => response.json())
.then(cart => {
// Update data layer
window.dataLayer.push({
event: 'removeFromCart',
cart: cart
});
});
}
Testing and Validation
Verify Data Layer
// Check data layer in console
console.log('Data Layer:', window.dataLayer);
// Validate structure
function validateDataLayer() {
const data = window.dataLayer?.[0];
if (!data) {
console.error('Data layer not found');
return false;
}
const required = ['pageType'];
const missing = required.filter(field => !data[field]);
if (missing.length > 0) {
console.error('Missing required fields:', missing);
return false;
}
console.log('✓ Data layer valid');
return true;
}
validateDataLayer();
Data Layer Inspector
// Create visual inspector
function inspectDataLayer() {
const data = window.dataLayer?.[0];
console.group('Data Layer Inspector');
console.log('Page Type:', data?.pageType);
console.log('Product:', data?.product);
console.log('Cart:', data?.cart);
console.log('User:', data?.user);
console.log('Transaction:', data?.transaction);
console.groupEnd();
}
// Run on page load
inspectDataLayer();
Best Practices
- Initialize Early - Load data layer before any tracking tags
- Single Source of Truth - Use data layer for all marketing tags
- Consistent Format - Maintain same structure across all pages
- Privacy First - Don't include sensitive data in plain text
- Validate Data - Implement validation before pushing to data layer
- Document Schema - Maintain documentation of data layer structure
- Version Control - Track changes to data layer schema
Next Steps
- Event Tracking - Fire Criteo events using data layer
- Cross-Domain Tracking - Share data across domains
- Troubleshooting - Debug data layer issues