The data layer is a JavaScript object that holds structured data about your CS-Cart store, products, and customer interactions. It enables powerful tracking through Google Tag Manager without hardcoding values in tags.
What is a Data Layer?
The data layer acts as a bridge between your CS-Cart store and Google Tag Manager. It standardizes how data is passed to your analytics and marketing tools.
Benefits
- Flexibility - Change tracking without modifying data layer
- Consistency - Same data structure across all tags
- Debugging - Easy to inspect data in browser console
- Version Control - Track data structure changes
- Multiple Tools - One data layer feeds all marketing pixels
Data Layer Structure
Basic Format
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'event_name',
'ecommerce': {
// Ecommerce data here
},
// Additional custom data
});
Implementation in CS-Cart
Step 1: Initialize Data Layer
Add this to your theme header, BEFORE the GTM container code.
File: design/themes/[your_theme]/templates/blocks/head_scripts.tpl
{* Initialize Data Layer *}
{literal}
<script>
window.dataLayer = window.dataLayer || [];
</script>
{/literal}
Step 2: Add Page-Level Data
Include basic page information on every page.
File: design/themes/[your_theme]/templates/index.tpl (or your main layout file)
{literal}
<script>
dataLayer.push({
'pageType': '{/literal}{$runtime.controller}{literal}',
'pageMode': '{/literal}{$runtime.mode}{literal}',
'customerGroup': '{/literal}{$auth.user_type|default:"guest"}{literal}',
'userLoggedIn': {/literal}{if $auth.user_id}true{else}false{/if}{literal},
{/literal}
{if $auth.user_id}
'userId': '{$auth.user_id}',
{/if}
{literal}
'currency': '{/literal}{$currencies.$primary_currency.currency_code|default:"USD"}{literal}',
'language': '{/literal}{$smarty.const.CART_LANGUAGE}{literal}'
});
</script>
{/literal}
Ecommerce Events
1. Product Impressions (Category/Search Pages)
Track when products appear in listings.
File: design/themes/[your_theme]/templates/views/categories/view.tpl
{if $products}
{literal}
<script>
dataLayer.push({
'event': 'view_item_list',
'ecommerce': {
'items': [
{/literal}
{foreach from=$products item=product name=products}
{
'item_id': '{$product.product_id}',
'item_name': '{$product.product|escape:javascript}',
'item_category': '{$category_data.category|escape:javascript}',
'price': {$product.price|default:0},
'currency': '{$currencies.$primary_currency.currency_code|default:"USD"}',
'item_list_name': '{$category_data.category|escape:javascript}',
'item_list_id': '{$category_data.category_id}',
'index': {$smarty.foreach.products.iteration}
{if $product.brand}
,'item_brand': '{$product.brand|escape:javascript}'
{/if}
}{if !$smarty.foreach.products.last},{/if}
{/foreach}
{literal}
]
}
});
</script>
{/literal}
{/if}
2. Product Detail View
Track product detail page views.
File: design/themes/[your_theme]/templates/views/products/view.tpl
{literal}
<script>
dataLayer.push({
'event': 'view_item',
'ecommerce': {
'currency': '{/literal}{$currencies.$primary_currency.currency_code|default:"USD"}{literal}',
'value': {/literal}{$product.price|default:0}{literal},
'items': [{
'item_id': '{/literal}{$product.product_id}{literal}',
'item_name': '{/literal}{$product.product|escape:javascript}{literal}',
'price': {/literal}{$product.price|default:0}{literal},
{/literal}
{if $product.main_category}
'item_category': '{$product.main_category|escape:javascript}',
{/if}
{if $product.brand}
'item_brand': '{$product.brand|escape:javascript}',
{/if}
{if $product.product_code}
'item_variant': '{$product.product_code}',
{/if}
{literal}
'quantity': 1
}]
}
});
</script>
{/literal}
3. Add to Cart
Track cart additions using CS-Cart's AJAX response.
File: Create custom JavaScript file js/addons/custom_datalayer/func.js
(function(_, $) {
// Hook into CS-Cart AJAX cart updates
$.ceEvent('on', 'ce.commoninit', function(context) {
// Listen for add to cart clicks
$(context).on('click', '.cm-submit[name="dispatch[checkout.add]"]', function() {
var $form = $(this).closest('form');
var productId = $form.find('input[name="product_id"]').val();
// Get product data from page
var productData = {
'item_id': productId,
'item_name': $form.find('.ty-product-block-title').text().trim() || 'Unknown',
'price': parseFloat($form.find('.ty-price-num').text().replace(/[^0-9.]/g, '')) || 0,
'quantity': parseInt($form.find('input[name="product_data[' + productId + '][amount]"]').val()) || 1
};
// Push to data layer
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'event': 'add_to_cart',
'ecommerce': {
'currency': 'USD', // Use your currency
'value': productData.price * productData.quantity,
'items': [productData]
}
});
});
});
// Alternative: Hook into AJAX response
$(document).ajaxComplete(function(event, xhr, settings) {
if (settings.url.indexOf('checkout.add') !== -1) {
try {
var response = JSON.parse(xhr.responseText);
if (response.cart_products) {
// Extract latest added product
var products = Object.values(response.cart_products);
var latestProduct = products[products.length - 1];
dataLayer.push({
'event': 'add_to_cart',
'ecommerce': {
'currency': latestProduct.currency || 'USD',
'value': latestProduct.display_price * latestProduct.amount,
'items': [{
'item_id': latestProduct.product_id,
'item_name': latestProduct.product,
'price': latestProduct.display_price,
'quantity': latestProduct.amount
}]
}
});
}
} catch (e) {
console.error('Error parsing cart response:', e);
}
}
});
}(Tygh, Tygh.$));
4. Remove from Cart
Track cart removals.
(function(_, $) {
$.ceEvent('on', 'ce.commoninit', function(context) {
$(context).on('click', '.ty-cart-content__delete', function() {
var $cartItem = $(this).closest('.ty-cart-items__item');
var productId = $cartItem.find('input[name*="cart_id"]').data('cart-id');
var productName = $cartItem.find('.product-title').text().trim();
var price = parseFloat($cartItem.find('.ty-price-num').text().replace(/[^0-9.]/g, ''));
var quantity = parseInt($cartItem.find('input[type="text"]').val()) || 1;
dataLayer.push({
'event': 'remove_from_cart',
'ecommerce': {
'currency': 'USD',
'value': price * quantity,
'items': [{
'item_id': productId,
'item_name': productName,
'price': price,
'quantity': quantity
}]
}
});
});
});
}(Tygh, Tygh.$));
5. View Cart
Track cart page views.
File: design/themes/[your_theme]/templates/views/checkout/cart.tpl
{if $cart_products}
{literal}
<script>
dataLayer.push({
'event': 'view_cart',
'ecommerce': {
'currency': '{/literal}{$currencies.$primary_currency.currency_code|default:"USD"}{literal}',
'value': {/literal}{$cart.total|default:0}{literal},
'items': [
{/literal}
{foreach from=$cart_products item=product name=cart_products}
{
'item_id': '{$product.product_id}',
'item_name': '{$product.product|escape:javascript}',
'price': {$product.price|default:0},
'quantity': {$product.amount|default:1}
{if $product.product_code}
,'item_variant': '{$product.product_code}'
{/if}
}{if !$smarty.foreach.cart_products.last},{/if}
{/foreach}
{literal}
]
}
});
</script>
{/literal}
{/if}
6. Begin Checkout
Track checkout initiation.
File: design/themes/[your_theme]/templates/views/checkout/checkout.tpl
{if $cart_products && $runtime.mode == 'checkout'}
{literal}
<script>
dataLayer.push({
'event': 'begin_checkout',
'ecommerce': {
'currency': '{/literal}{$currencies.$primary_currency.currency_code|default:"USD"}{literal}',
'value': {/literal}{$cart.total|default:0}{literal},
{/literal}
{if $cart.applied_promotions}
'coupon': '{$cart.applied_promotions|@key}',
{/if}
{literal}
'items': [
{/literal}
{foreach from=$cart_products item=product name=cart_products}
{
'item_id': '{$product.product_id}',
'item_name': '{$product.product|escape:javascript}',
'price': {$product.price|default:0},
'quantity': {$product.amount|default:1}
}{if !$smarty.foreach.cart_products.last},{/if}
{/foreach}
{literal}
]
}
});
</script>
{/literal}
{/if}
7. Add Shipping Info
Track shipping method selection.
(function(_, $) {
$.ceEvent('on', 'ce.commoninit', function(context) {
$(context).on('change', 'input[name^="shipping_ids"]', function() {
var shippingMethod = $(this).closest('label').find('.ty-shipping-option__title').text().trim();
var shippingCost = parseFloat($(this).data('shipping-cost')) || 0;
dataLayer.push({
'event': 'add_shipping_info',
'ecommerce': {
'currency': 'USD',
'value': shippingCost,
'shipping_tier': shippingMethod
}
});
});
});
}(Tygh, Tygh.$));
8. Add Payment Info
Track payment method selection.
(function(_, $) {
$.ceEvent('on', 'ce.commoninit', function(context) {
$(context).on('change', 'input[name="payment_id"]', function() {
var paymentMethod = $(this).closest('label').find('.ty-payment-option__title').text().trim();
dataLayer.push({
'event': 'add_payment_info',
'ecommerce': {
'currency': 'USD',
'payment_type': paymentMethod
}
});
});
});
}(Tygh, Tygh.$));
9. Purchase Event
The most critical event - track completed transactions.
File: design/themes/[your_theme]/templates/views/checkout/components/order_notification.tpl
{if $order_info}
{literal}
<script>
// Prevent duplicate tracking
var orderId = '{/literal}{$order_info.order_id}{literal}';
var trackedKey = 'tracked_purchase_' + orderId;
if (!sessionStorage.getItem(trackedKey)) {
dataLayer.push({
'event': 'purchase',
'ecommerce': {
'transaction_id': orderId,
'value': {/literal}{$order_info.total|default:0}{literal},
'tax': {/literal}{$order_info.tax_subtotal|default:0}{literal},
'shipping': {/literal}{$order_info.shipping_cost|default:0}{literal},
'currency': '{/literal}{$order_info.secondary_currency|default:$order_info.currency|default:"USD"}{literal}',
{/literal}
{if $order_info.promotions}
'coupon': '{$order_info.promotions|@key}',
{/if}
{literal}
'items': [
{/literal}
{foreach from=$order_info.products item=product name=order_products}
{
'item_id': '{$product.product_id}',
'item_name': '{$product.product|escape:javascript}',
'price': {$product.price|default:0},
'quantity': {$product.amount|default:1}
{if $product.product_code}
,'item_variant': '{$product.product_code}'
{/if}
{if $product.product_options}
,'item_category': '{foreach from=$product.product_options item=option}{$option.variant_name} {/foreach}'
{/if}
}{if !$smarty.foreach.order_products.last},{/if}
{/foreach}
{literal}
]
}
});
// Mark as tracked
sessionStorage.setItem(trackedKey, 'true');
}
</script>
{/literal}
{/if}
Multi-Vendor Data Layer
For CS-Cart Multi-Vendor marketplace, include vendor information.
Vendor Attribution
{literal}
'items': [{
'item_id': '{/literal}{$product.product_id}{literal}',
'item_name': '{/literal}{$product.product|escape:javascript}{literal}',
'price': {/literal}{$product.price}{literal},
'quantity': {/literal}{$product.amount}{literal},
// Vendor-specific data
'affiliation': '{/literal}{$product.company_name|escape:javascript}{literal}',
'item_brand': '{/literal}{$product.company_name|escape:javascript}{literal}',
'vendor_id': '{/literal}{$product.company_id}{literal}'
}]
{/literal}
Vendor Page Tracking
{if $runtime.controller == 'companies' && $runtime.mode == 'view'}
{literal}
<script>
dataLayer.push({
'event': 'view_vendor',
'vendorId': '{/literal}{$company_data.company_id}{literal}',
'vendorName': '{/literal}{$company_data.company|escape:javascript}{literal}',
'vendorProducts': {/literal}{$company_data.products_count|default:0}{literal}
});
</script>
{/literal}
{/if}
Custom Events
User Registration
{if $user_data.user_id && $runtime.controller == 'profiles' && $runtime.mode == 'add'}
{literal}
<script>
dataLayer.push({
'event': 'sign_up',
'method': 'website_registration',
'user_id': '{/literal}{$user_data.user_id}{literal}'
});
</script>
{/literal}
{/if}
Newsletter Signup
$(document).on('submit', '#subscribe_form', function() {
dataLayer.push({
'event': 'newsletter_signup',
'method': 'footer_form'
});
});
Search
{if $search.q}
{literal}
<script>
dataLayer.push({
'event': 'search',
'search_term': '{/literal}{$search.q|escape:javascript}{literal}',
'search_results': {/literal}{$products|count}{literal}
});
</script>
{/literal}
{/if}
Wishlist
$(document).on('click', '.cm-add-to-wish-list', function() {
var productId = $(this).data('product-id');
var productName = $(this).data('product-name');
var productPrice = parseFloat($(this).data('product-price'));
dataLayer.push({
'event': 'add_to_wishlist',
'ecommerce': {
'currency': 'USD',
'value': productPrice,
'items': [{
'item_id': productId,
'item_name': productName,
'price': productPrice
}]
}
});
});
Using Data Layer in GTM
Create Data Layer Variables
- In GTM, go to Variables → New
- Select Data Layer Variable
- Name: "DL - Event"
- Data Layer Variable Name:
event - Save
Repeat for other variables:
- DL - Ecommerce Items:
ecommerce.items - DL - Transaction ID:
ecommerce.transaction_id - DL - Transaction Value:
ecommerce.value - DL - User ID:
userId - DL - Page Type:
pageType
Create Triggers Using Data Layer
Purchase Trigger:
- Triggers → New
- Type: Custom Event
- Event name:
purchase - Save
Add to Cart Trigger:
- Triggers → New
- Type: Custom Event
- Event name:
add_to_cart - Save
Create Tags Using Data Layer Variables
GA4 Purchase Tag:
- Tags → New
- Type: Google Analytics: GA4 Event
- Configuration Tag: [Your GA4 Config]
- Event Name:
purchase - Event Parameters:
transaction_id:\{\{DL - Transaction ID\}\}value:\{\{DL - Transaction Value\}\}
- Trigger: Purchase (custom event)
- Save
Debugging Data Layer
Console Inspection
// View entire data layer
console.log(window.dataLayer);
// View specific event
console.log(dataLayer.filter(obj => obj.event === 'purchase'));
// Monitor data layer pushes
var originalPush = dataLayer.push;
dataLayer.push = function() {
console.log('DataLayer Push:', arguments);
return originalPush.apply(dataLayer, arguments);
};
GTM Preview Mode
- Click Preview in GTM
- Connect to your CS-Cart store
- View Data Layer tab
- Inspect values for each event
- Verify correct data structure
Browser Extensions
- Google Tag Assistant - Verify GTM implementation
- dataLayer Inspector - Real-time data layer monitoring
- GA Debugger - Check GA4 event parameters
Best Practices
1. Data Structure Consistency
Use the same field names across all events:
item_id(notproduct_idin some events anditem_idin others)item_name(notproduct_name)- Consistent currency codes
2. Error Handling
Wrap data layer pushes in try-catch:
try {
dataLayer.push({
'event': 'add_to_cart',
'ecommerce': { /* ... */ }
});
} catch (error) {
console.error('DataLayer error:', error);
}
3. Default Values
Always provide defaults for optional data:
'price': {$product.price|default:0}
4. Sanitize Data
Escape JavaScript strings properly:
'item_name': '{$product.product|escape:javascript}'
5. Clear Ecommerce Object
Clear ecommerce data between events:
dataLayer.push({ ecommerce: null }); // Clear previous ecommerce data
dataLayer.push({
'event': 'purchase',
'ecommerce': { /* new data */ }
});
6. Don't Include PII
Never include:
- Email addresses
- Phone numbers
- Full names
- Payment details
- Passwords
Performance Optimization
Lazy Loading
For product lists, use Intersection Observer:
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
// Push impression to data layer
dataLayer.push({ /* impression data */ });
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('.product-item').forEach(function(item) {
observer.observe(item);
});
Debouncing
For rapid events (scroll, filter changes):
var debounce = function(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
};
};
$(document).on('change', '.filter-checkbox', debounce(function() {
dataLayer.push({ /* filter data */ });
}, 500));
Next Steps
- Configure GTM Tags - Set up tags using data layer
- GA4 Setup - Connect GA4 to data layer
- Troubleshooting - Debug data layer issues