Data Layer Implementation for CS-Cart with GTM | OpsBlu Docs

Data Layer Implementation for CS-Cart with GTM

Complete guide to implementing a robust data layer in CS-Cart for Google Tag Manager ecommerce tracking and event management

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'
  });
});
{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

  1. In GTM, go to Variables → New
  2. Select Data Layer Variable
  3. Name: "DL - Event"
  4. Data Layer Variable Name: event
  5. 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:

  1. Triggers → New
  2. Type: Custom Event
  3. Event name: purchase
  4. Save

Add to Cart Trigger:

  1. Triggers → New
  2. Type: Custom Event
  3. Event name: add_to_cart
  4. Save

Create Tags Using Data Layer Variables

GA4 Purchase Tag:

  1. Tags → New
  2. Type: Google Analytics: GA4 Event
  3. Configuration Tag: [Your GA4 Config]
  4. Event Name: purchase
  5. Event Parameters:
    • transaction_id: \{\{DL - Transaction ID\}\}
    • value: \{\{DL - Transaction Value\}\}
  6. Trigger: Purchase (custom event)
  7. 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

  1. Click Preview in GTM
  2. Connect to your CS-Cart store
  3. View Data Layer tab
  4. Inspect values for each event
  5. 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 (not product_id in some events and item_id in others)
  • item_name (not product_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

Additional Resources