Implement a comprehensive data layer for Google Tag Manager on your 3dcart/Shift4Shop store to capture page context, user data, and ecommerce events.
Data Layer Overview
A data layer is a JavaScript object that passes information from your website to GTM. For 3dcart/Shift4Shop, you'll combine:
- Standard page data
- 3dcart template variables
- Ecommerce information
- User/customer data
Base Data Layer Structure
Initialize Data Layer
Add to Global Footer (BEFORE GTM container code):
<script>
window.dataLayer = window.dataLayer || [];
</script>
Page-Level Data Layer
Add page context data for all pages:
<script>
window.dataLayer = window.dataLayer || [];
// Detect page type
var pageType = 'other';
var path = window.location.pathname.toLowerCase();
if (path === '/' || path === '/default.asp') {
pageType = 'home';
} else if (path.indexOf('/product') !== -1) {
pageType = 'product';
} else if (path.indexOf('/category') !== -1) {
pageType = 'category';
} else if (path.indexOf('/cart') !== -1) {
pageType = 'cart';
} else if (path.indexOf('/checkout') !== -1) {
pageType = 'checkout';
} else if (path.indexOf('/receipt') !== -1 || path.indexOf('/thankyou') !== -1) {
pageType = 'confirmation';
}
// Push page data
dataLayer.push({
'event': 'page_data',
'pageType': pageType,
'pagePath': window.location.pathname,
'pageTitle': document.title,
'pageUrl': window.location.href
});
</script>
Page-Specific Data Layers
Homepage Data Layer
<script>
// Only on homepage
if (window.location.pathname === '/' || window.location.pathname === '/default.asp') {
dataLayer.push({
'event': 'homepage_view',
'pageType': 'home'
});
}
</script>
Product Page Data Layer
Use 3dcart template variables to capture product information:
<script>
// Only on product pages
if (window.location.pathname.indexOf('/product') !== -1) {
dataLayer.push({
'event': 'product_detail_view',
'pageType': 'product',
'ecommerce': {
'currency': 'USD',
'value': parseFloat('[productprice]'),
'items': [{
'item_id': '[productid]',
'item_name': '[productname]',
'item_brand': '[manufacturer]',
'item_category': '[categoryname]',
'price': parseFloat('[productprice]'),
'quantity': 1
}]
},
'product': {
'id': '[productid]',
'name': '[productname]',
'sku': '[productsku]',
'price': parseFloat('[productprice]'),
'category': '[categoryname]',
'manufacturer': '[manufacturer]',
'inStock': '[stockstatus]' // If available
}
});
}
</script>
Category Page Data Layer
<script>
// Only on category pages
if (window.location.pathname.indexOf('/category') !== -1) {
dataLayer.push({
'event': 'category_view',
'pageType': 'category',
'category': {
'id': '[categoryid]',
'name': '[categoryname]'
}
});
// Optional: Track product impressions
// Parse products from page and push to data layer
var products = [];
var productElements = document.querySelectorAll('.product-item, .product-box');
productElements.forEach(function(element, index) {
var productId = element.getAttribute('data-product-id');
var productName = element.querySelector('.product-name')?.textContent;
var productPrice = element.querySelector('.product-price')?.textContent;
if (productId && productName) {
products.push({
'item_id': productId,
'item_name': productName.trim(),
'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '')) || 0,
'index': index,
'item_list_name': '[categoryname]',
'item_list_id': '[categoryid]'
});
}
});
if (products.length > 0) {
dataLayer.push({
'event': 'view_item_list',
'ecommerce': {
'items': products.slice(0, 20) // Limit to first 20
}
});
}
}
</script>
Cart Page Data Layer
<script>
// Only on cart page
if (window.location.pathname.indexOf('/cart') !== -1) {
// Parse cart items from page
var cartItems = [];
var cartRows = document.querySelectorAll('.cart-item, table.cart tbody tr');
cartRows.forEach(function(row) {
var productId = row.getAttribute('data-product-id') || '';
var productName = row.querySelector('.product-name, .item-name')?.textContent || '';
var productPrice = row.querySelector('.price, .item-price')?.textContent || '0';
var quantity = row.querySelector('input[name="quantity"]')?.value || '1';
if (productName) {
cartItems.push({
'item_id': productId,
'item_name': productName.trim(),
'price': parseFloat(productPrice.replace(/[^0-9.]/g, '')),
'quantity': parseInt(quantity)
});
}
});
// Get cart total
var cartTotalElement = document.querySelector('.cart-total, .total-amount');
var cartTotal = cartTotalElement ? parseFloat(cartTotalElement.textContent.replace(/[^0-9.]/g, '')) : 0;
dataLayer.push({
'event': 'cart_view',
'pageType': 'cart',
'ecommerce': {
'currency': 'USD',
'value': cartTotal,
'items': cartItems
},
'cart': {
'total': cartTotal,
'itemCount': cartItems.length
}
});
}
</script>
Checkout Page Data Layer
<script>
// Only on checkout pages
if (window.location.pathname.indexOf('/checkout') !== -1) {
dataLayer.push({
'event': 'checkout_view',
'pageType': 'checkout'
});
// Optional: Add checkout items if accessible
var checkoutItems = [];
var itemRows = document.querySelectorAll('.checkout-item, .order-item');
itemRows.forEach(function(row) {
var productName = row.querySelector('.product-name, .item-name')?.textContent;
var productPrice = row.querySelector('.price')?.textContent;
var quantity = row.querySelector('.quantity, .qty')?.textContent;
if (productName) {
checkoutItems.push({
'item_name': productName.trim(),
'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0'),
'quantity': parseInt(quantity?.replace(/[^0-9]/g, '') || '1')
});
}
});
if (checkoutItems.length > 0) {
var orderTotal = document.querySelector('.order-total, .total-amount');
var total = orderTotal ? parseFloat(orderTotal.textContent.replace(/[^0-9.]/g, '')) : 0;
dataLayer.push({
'event': 'begin_checkout',
'ecommerce': {
'currency': 'USD',
'value': total,
'items': checkoutItems
}
});
}
}
</script>
Order Confirmation Data Layer
Critical for purchase tracking:
<script>
// Only on order confirmation/receipt page
if (window.location.pathname.indexOf('/receipt') !== -1 ||
window.location.pathname.indexOf('/thankyou') !== -1) {
var orderId = '[invoicenumber]';
// Check if already tracked
if (!sessionStorage.getItem('purchase_tracked_' + orderId)) {
// Parse order items
var orderItems = [];
var itemRows = document.querySelectorAll('.order-item, .receipt-item');
itemRows.forEach(function(row) {
var productName = row.querySelector('.product-name, .item-name')?.textContent;
var productSku = row.querySelector('.sku')?.textContent;
var productPrice = row.querySelector('.price')?.textContent;
var quantity = row.querySelector('.quantity, .qty')?.textContent;
if (productName) {
orderItems.push({
'item_id': productSku?.trim() || '',
'item_name': productName.trim(),
'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0'),
'quantity': parseInt(quantity?.replace(/[^0-9]/g, '') || '1')
});
}
});
// Push purchase event
dataLayer.push({
'event': 'purchase',
'pageType': 'confirmation',
'ecommerce': {
'transaction_id': orderId,
'affiliation': 'Online Store',
'value': parseFloat('[invoicetotal]'),
'currency': 'USD',
'tax': parseFloat('[invoicetax]'),
'shipping': parseFloat('[invoiceshipping]'),
'coupon': '[couponcode]',
'items': orderItems
},
'transaction': {
'id': orderId,
'total': parseFloat('[invoicetotal]'),
'tax': parseFloat('[invoicetax]'),
'shipping': parseFloat('[invoiceshipping]'),
'coupon': '[couponcode]'
}
});
// Mark as tracked
sessionStorage.setItem('purchase_tracked_' + orderId, 'true');
}
}
</script>
User/Customer Data Layer
Customer Information
Add customer data when available:
<script>
// Check if customer is logged in
var customerId = '[customerid]';
if (customerId && customerId !== '[customerid]') {
dataLayer.push({
'event': 'customer_data',
'user': {
'id': customerId,
'type': '[customertypeid]',
'logged_in': true
}
});
} else {
dataLayer.push({
'event': 'customer_data',
'user': {
'logged_in': false
}
});
}
</script>
Event-Based Data Layer Pushes
Add to Cart Event
<script>
document.addEventListener('DOMContentLoaded', function() {
var addToCartForms = document.querySelectorAll('form[action*="addtocart"]');
addToCartForms.forEach(function(form) {
form.addEventListener('submit', function() {
var quantity = form.querySelector('input[name="quantity"]')?.value || 1;
dataLayer.push({
'event': 'add_to_cart',
'ecommerce': {
'currency': 'USD',
'value': parseFloat('[productprice]') * parseInt(quantity),
'items': [{
'item_id': '[productid]',
'item_name': '[productname]',
'price': parseFloat('[productprice]'),
'quantity': parseInt(quantity)
}]
}
});
});
});
});
</script>
Remove from Cart Event
<script>
document.addEventListener('DOMContentLoaded', function() {
if (window.location.pathname.indexOf('/cart') !== -1) {
var removeButtons = document.querySelectorAll('.remove-item, a[href*="remove"]');
removeButtons.forEach(function(button) {
button.addEventListener('click', function() {
var cartRow = button.closest('tr, .cart-item');
var productName = cartRow?.querySelector('.product-name')?.textContent;
var productPrice = cartRow?.querySelector('.price')?.textContent;
var quantity = cartRow?.querySelector('input[name="quantity"]')?.value || 1;
if (productName) {
dataLayer.push({
'event': 'remove_from_cart',
'ecommerce': {
'currency': 'USD',
'value': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0') * parseInt(quantity),
'items': [{
'item_name': productName.trim(),
'price': parseFloat(productPrice?.replace(/[^0-9.]/g, '') || '0'),
'quantity': parseInt(quantity)
}]
}
});
}
});
});
}
});
</script>
GTM Variables for 3dcart Data Layer
Create Data Layer Variables in GTM
For each data point in your data layer, create corresponding GTM variables.
Page Type Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
pageType - Variable Name:
DLV - Page Type
Product ID Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
product.id - Variable Name:
DLV - Product ID
Product Name Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
product.name - Variable Name:
DLV - Product Name
Product Price Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
product.price - Variable Name:
DLV - Product Price
Category ID Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
category.id - Variable Name:
DLV - Category ID
Category Name Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
category.name - Variable Name:
DLV - Category Name
Cart Total Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
cart.total - Variable Name:
DLV - Cart Total
Transaction ID Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
transaction.id - Variable Name:
DLV - Transaction ID
User ID Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
user.id - Variable Name:
DLV - User ID
Ecommerce Items Variable
- Variable Type: Data Layer Variable
- Data Layer Variable Name:
ecommerce.items - Variable Name:
DLV - Ecommerce Items
Creating GTM Triggers Based on Data Layer
Product View Trigger
- Trigger Type: Custom Event
- Event name:
product_detail_view - This trigger fires on: All Custom Events
- Trigger Name: Event - Product View
Category View Trigger
- Trigger Type: Custom Event
- Event name:
category_view - Trigger Name: Event - Category View
Add to Cart Trigger
- Trigger Type: Custom Event
- Event name:
add_to_cart - Trigger Name: Event - Add to Cart
Remove from Cart Trigger
- Trigger Type: Custom Event
- Event name:
remove_from_cart - Trigger Name: Event - Remove from Cart
Purchase Trigger
- Trigger Type: Custom Event
- Event name:
purchase - Trigger Name: Event - Purchase
Complete Data Layer Example
Here's a complete data layer implementation for Global Footer:
<script>
// Initialize data layer
window.dataLayer = window.dataLayer || [];
// Detect page type
var pageType = 'other';
var path = window.location.pathname.toLowerCase();
if (path === '/' || path === '/default.asp') pageType = 'home';
else if (path.indexOf('/product') !== -1) pageType = 'product';
else if (path.indexOf('/category') !== -1) pageType = 'category';
else if (path.indexOf('/cart') !== -1) pageType = 'cart';
else if (path.indexOf('/checkout') !== -1) pageType = 'checkout';
else if (path.indexOf('/receipt') !== -1 || path.indexOf('/thankyou') !== -1) pageType = 'confirmation';
// Push base data
dataLayer.push({
'pageType': pageType,
'pagePath': window.location.pathname,
'pageTitle': document.title
});
// Product page data
if (pageType === 'product') {
dataLayer.push({
'event': 'product_detail_view',
'ecommerce': {
'currency': 'USD',
'value': parseFloat('[productprice]'),
'items': [{
'item_id': '[productid]',
'item_name': '[productname]',
'price': parseFloat('[productprice]'),
'item_category': '[categoryname]',
'quantity': 1
}]
}
});
}
// Order confirmation data
if (pageType === 'confirmation') {
var orderId = '[invoicenumber]';
if (!sessionStorage.getItem('purchase_tracked_' + orderId)) {
dataLayer.push({
'event': 'purchase',
'ecommerce': {
'transaction_id': orderId,
'value': parseFloat('[invoicetotal]'),
'currency': 'USD',
'tax': parseFloat('[invoicetax]'),
'shipping': parseFloat('[invoiceshipping]'),
'items': [] // Parse from page
}
});
sessionStorage.setItem('purchase_tracked_' + orderId, 'true');
}
}
// Customer data
var customerId = '[customerid]';
if (customerId && customerId !== '[customerid]') {
dataLayer.push({
'user': {
'id': customerId,
'logged_in': true
}
});
}
</script>
Testing the Data Layer
Browser Console Testing
// View entire data layer
console.table(window.dataLayer);
// View specific event
console.log(window.dataLayer.filter(item => item.event === 'purchase'));
// Monitor data layer pushes
const originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
console.log('DataLayer Push:', arguments[0]);
originalPush.apply(window.dataLayer, arguments);
};
GTM Preview Mode
- Enable Preview mode in GTM
- Visit each page type
- Check "Data Layer" tab in Tag Assistant
- Verify variables populate correctly
- Confirm events fire as expected
Debug Checklist
- ✓ Data layer initializes before GTM
- ✓ Page type detected correctly
- ✓ Template variables populate (not showing [variable] literal)
- ✓ Numeric values are parsed (not strings with currency symbols)
- ✓ Events fire on correct user actions
- ✓ Items array structure is correct
- ✓ No duplicate events
Common Issues and Solutions
Template Variables Show Literal Text
Problem: Data layer shows [productid] instead of actual ID
Cause: Template variable used on wrong page type
Fix: Add page type checks:
if (pageType === 'product') {
// Only use product variables here
}
Data Layer Undefined
Problem: window.dataLayer is undefined
Fix: Initialize before GTM:
window.dataLayer = window.dataLayer || [];
Duplicate Events
Problem: Same event fires multiple times
Cause: Multiple data layer pushes or page reloads
Fix: Use sessionStorage for deduplication:
if (!sessionStorage.getItem('event_tracked')) {
dataLayer.push({...});
sessionStorage.setItem('event_tracked', 'true');
}
Values Are Strings, Not Numbers
Problem: Prices showing as "$29.99" instead of 29.99
Fix: Parse currency values:
value: parseFloat('[productprice]') // Correct
value: '[productprice]' // Wrong
Next Steps
- GA4 Ecommerce Tracking - Use data layer for GA4
- Meta Pixel Setup - Use data layer for Meta Pixel
- Troubleshoot Events Not Firing - Debug data layer issues
Additional Resources
- GTM Data Layer Documentation - Google
- 3dcart Template Variables (Shift4Shop help articles are no longer available; see your store's admin panel for template variable reference)
- Ecommerce Data Layer - Google