Tracking Shopify stores through GTM provides a lot of flexibility and manageability. However, effective Shopify tracking via GTM necessitates a solid Shopify DataLayer code. Unfortunately, scouring the internet won’t yield a complete Shopify data layer capable of comprehensively tracking every Shopify event. In response, I’ve recently pioneered a groundbreaking Shopify DataLayer code. This code boasts comprehensive features and remarkable flexibility to track all Shopify events seamlessly. Rigorously tested across various Shopify themes and advanced features like Quick View, Add To Wish List, Sticky Cart, Cart Drawer, and more. The data layer code is finely tuned for optimal performance without any adverse effects on your Shopify store.
In the upcoming tutorial, I delve into the intricacies of this remarkable Shopify Data Layer. By carefully following this tutorial, worries about tracking Shopify via GTM will become a thing of the past.
With the Shopify DataLayer, you can keep track every type of Shopify event, including Google Recommend events and more.
Events you can track with this Shopify DataLayer
No | Event Name | Description |
---|---|---|
1 | view_item | When view a product. This Shopify datalayer works for both product page view and view product on quick view |
2 | view_item_list | When product category page visited. Also customized solution available for view item list on product recommendations |
3 | select_item | When product select from list or change product variant |
4 | add_to_cart | When a product is added to cart or increment quantity of a product from cart. The Shopify DataLayer works Quick View, Sticky Cart, Direct Checkout, Add to Cart form Single Product page, add to cart from product category page, etc |
5 | remove_from_cart | When a item is removed from cart. The Shopify DataLayer works for Mini Cart and Dropdown Cart, Cart Drawer, Cart Page |
6 | add_to_wishlist | When custom add a product to Wishlist basket |
7 | view_cart | When cart is viewed. This Shopify dataLayer works for for Mini Cart and Dropdown Cart, Cart Drawer, Cart Page |
8 | begin_checkout | When click begin checkout button to go to checkout page |
9 | add_payment_info | When payment information is given on checkout page |
10 | add_shopping_info | When shipping information is given on checkout page |
11 | purchase | When purchase successful |
newsletter_signup | When signup for a newsletter form from Shopify Store | |
12 | contact_form_submit | When contact form is submitted from Shopify Store |
phone_number_click | When phone number is clicked from Shopify Store | |
13 | email_click | When email is click from Shopify Store |
14 | search (Ajax) | search_term, items[item_id , variant_id, sku, product_id, item_name, index , brand, item_category , price , quantity] |
15 | login | When customer login |
16 | sign_up | When customer signup |
Ecommerc: transaction_id, currency, value, coupon, shipping, tax, items array, item_list_id, item_list_name,
Form: form_id, email, phone number, first name, last name, other user data
Customer’s Data: First Name, Last Name, Email, Hashed Email, Phone Number, Hashed Phone Number, Address
Video Tutorial Of this Shopify DataLayer
ultimate-datalayer.liquid
<script>
/**
* Author: Md Hasanuzzamna
* Email: info@leomeasure.com
* Linkedin: https://linkedin.com/md-h
* Version: 3.0.0
* Last Update: 14 Jun 2024
*/
(function() {
class Ultimate_Shopify_DataLayer {
constructor() {
window.dataLayer = window.dataLayer || [];
// use a prefix of events name
this.eventPrefix = '';
//Keep the value false to get non-formatted product ID
this.formattedItemId = true;
// data schema
this.dataSchema = {
ecommerce: {
show: true
},
dynamicRemarketing: {
show: false,
business_vertical: 'retail'
}
}
// add to wishlist selectors
this.addToWishListSelectors = {
'addWishListIcon': '',
'gridItemSelector': '',
'productLinkSelector': 'a[href*="/products/"]'
}
// quick view selectors
this.quickViewSelector = {
'quickViewElement': '',
'gridItemSelector': '',
'productLinkSelector': 'a[href*="/products/"]'
}
// mini cart button selector
this.miniCartButton = [
'a[href="/cart"]',
];
this.miniCartAppersOn = 'click';
// begin checkout buttons/links selectors
this.beginCheckoutButtons = [
'input[name="checkout"]',
'button[name="checkout"]',
'a[href="/checkout"]',
'.additional-checkout-buttons',
];
// direct checkout button selector
this.shopifyDirectCheckoutButton = [
'.shopify-payment-button'
]
//Keep the value true if Add to Cart redirects to the cart page
this.isAddToCartRedirect = false;
// keep the value false if cart items increment/decrement/remove refresh page
this.isAjaxCartIncrementDecrement = true;
// Caution: Do not modify anything below this line, as it may result in it not functioning correctly.
this.cart = {{ cart | json }}
this.countryCode = "{{ shop.address.country_code }}";
this.collectData();
this.storeURL = "{{ shop.secure_url }}";
localStorage.setItem('shopCountryCode', this.countryCode);
}
updateCart() {
fetch("/cart.js")
.then((response) => response.json())
.then((data) => {
this.cart = data;
});
}
debounce(delay) {
let timeoutId;
return function(func) {
const context = this;
const args = arguments;
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
func.apply(context, args);
}, delay);
};
}
eventConsole(eventName, eventData) {
const css1 = 'background: red; color: #fff; font-size: normal; border-radius: 3px 0 0 3px; padding: 3px 4px;';
const css2 = 'background-color: blue; color: #fff; font-size: normal; border-radius: 0 3px 3px 0; padding: 3px 4px;';
console.log('%cGTM DataLayer Event:%c' + eventName, css1, css2, eventData);
}
collectData() {
this.customerData();
this.ajaxRequestData();
this.searchPageData();
this.miniCartData();
this.beginCheckoutData();
{% if template contains 'cart' %}
this.viewCartPageData();
{% endif %}
{% if template contains 'product' %}
this.productSinglePage();
{% endif %}
{% if template contains 'collection' %}
this.collectionsPageData();
{% endif %}
this.addToWishListData();
this.quickViewData();
this.formData();
this.phoneClickData();
this.emailClickData();
}
//logged-in customer data
customerData() {
const currentUser = {};
{% if customer %}
currentUser.id = {{ customer.id }};
currentUser.first_name = "{{ customer.first_name }}";
currentUser.last_name = "{{ customer.last_name }}";
currentUser.full_name = "{{ customer.name }}";
currentUser.email = "{{ customer.email }}";
currentUser.phone = "{{ customer.default_address.phone }}";
{% if customer.default_address %}
currentUser.address = {
address_summary: "{{ customer.default_address.summary }}",
address1: "{{ customer.default_address.address1 }}",
address2: "{{ customer.default_address.address2 }}",
city: "{{ customer.default_address.city }}",
street: "{{ customer.default_address.street }}",
zip: "{{ customer.default_address.zip }}",
company: "{{ customer.default_address.company }}",
country: "{{ customer.default_address.country.name }}",
countryCode: "{{ customer.default_address.country_code }}",
province: "{{ customer.default_address.province }}"
};
{% endif %}
{% endif %}
if (currentUser.email) {
currentUser.hash_email = "{{ customer.email | sha256 }}"
}
if (currentUser.phone) {
currentUser.hash_phone = "{{ customer.phone | sha256 }}"
}
window.dataLayer = window.dataLayer || [];
dataLayer.push({
customer: currentUser
});
}
// add_to_cart, remove_from_cart, search
ajaxRequestData() {
const self = this;
// handle non-ajax add to cart
if(this.isAddToCartRedirect) {
document.addEventListener('submit', function(event) {
const addToCartForm = event.target.closest('form[action="/cart/add"]');
if(addToCartForm) {
event.preventDefault();
const formData = new FormData(addToCartForm);
fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
body: formData
})
.then(response => {
window.location.href = "{{ routes.cart_url }}";
})
.catch((error) => {
console.error('Error:', error);
});
}
});
}
// fetch
let originalFetch = window.fetch;
let debounce = this.debounce(800);
window.fetch = function () {
return originalFetch.apply(this, arguments).then((response) => {
if (response.ok) {
let cloneResponse = response.clone();
let requestURL = arguments[0]['url'] || arguments[0];
if(/.*\/search\/?.*\?.*q=.+/.test(requestURL) && !requestURL.includes('&requestFrom=uldt')) {
const queryString = requestURL.split('?')[1];
const urlParams = new URLSearchParams(queryString);
const search_term = urlParams.get("q");
debounce(function() {
fetch(`${self.storeURL}/search/suggest.json?q=${search_term}&resources[type]=product&requestFrom=uldt`)
.then(res => res.json())
.then(function(data) {
const products = data.resources.results.products;
if(products.length) {
const fetchRequests = products.map(product =>
fetch(`${self.storeURL}/${product.url.split('?')[0]}.js`)
.then(response => response.json())
.catch(error => console.error('Error fetching:', error))
);
Promise.all(fetchRequests)
.then(products => {
const items = products.map((product) => {
return {
product_id: product.id,
product_title: product.title,
variant_id: product.variants[0].id,
variant_title: product.variants[0].title,
vendor: product.vendor,
total_discount: 0,
final_price: product.price_min,
product_type: product.type,
quantity: 1
}
});
self.ecommerceDataLayer('search', {search_term, items});
})
}else {
self.ecommerceDataLayer('search', {search_term, items: []});
}
});
});
}
else if (requestURL.includes("/cart/add")) {
cloneResponse.text().then((text) => {
let data = JSON.parse(text);
if(data.items && Array.isArray(data.items)) {
data.items.forEach(function(item) {
self.ecommerceDataLayer('add_to_cart', {items: [item]});
})
} else {
self.ecommerceDataLayer('add_to_cart', {items: [data]});
}
self.updateCart();
});
}else if(requestURL.includes("/cart/change") || requestURL.includes("/cart/update")) {
cloneResponse.text().then((text) => {
let newCart = JSON.parse(text);
let newCartItems = newCart.items;
let oldCartItems = self.cart.items;
for(let i = 0; i < oldCartItems.length; i++) {
let item = oldCartItems[i];
let newItem = newCartItems.find(newItems => newItems.id === item.id);
if(newItem) {
if(newItem.quantity > item.quantity) {
// cart item increment
let quantity = (newItem.quantity - item.quantity);
let updatedItem = {...item, quantity}
self.ecommerceDataLayer('add_to_cart', {items: [updatedItem]});
self.updateCart();
}else if(newItem.quantity < item.quantity) {
// cart item decrement
let quantity = (item.quantity - newItem.quantity);
let updatedItem = {...item, quantity}
self.ecommerceDataLayer('remove_from_cart', {items: [updatedItem]});
self.updateCart();
}
}else {
self.ecommerceDataLayer('remove_from_cart', {items: [item]});
self.updateCart();
}
}
});
}
}
return response;
});
}
// end fetch
//xhr
var origXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function() {
var requestURL;
var xhr = new origXMLHttpRequest();
var origOpen = xhr.open;
var origSend = xhr.send;
// Override the `open` function.
xhr.open = function(method, url) {
requestURL = url;
return origOpen.apply(this, arguments);
};
xhr.send = function() {
// Only proceed if the request URL matches what we're looking for.
if (requestURL.includes("/cart/add") || requestURL.includes("/cart/change") || /.*\/search\/?.*\?.*q=.+/.test(requestURL)) {
xhr.addEventListener('load', function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 400) {
if(/.*\/search\/?.*\?.*q=.+/.test(requestURL) && !requestURL.includes('&requestFrom=uldt')) {
const queryString = requestURL.split('?')[1];
const urlParams = new URLSearchParams(queryString);
const search_term = urlParams.get("q");
debounce(function() {
fetch(`${self.storeURL}/search/suggest.json?q=${search_term}&resources[type]=product&requestFrom=uldt`)
.then(res => res.json())
.then(function(data) {
const products = data.resources.results.products;
if(products.length) {
const fetchRequests = products.map(product =>
fetch(`${self.storeURL}/${product.url.split('?')[0]}.js`)
.then(response => response.json())
.catch(error => console.error('Error fetching:', error))
);
Promise.all(fetchRequests)
.then(products => {
const items = products.map((product) => {
return {
product_id: product.id,
product_title: product.title,
variant_id: product.variants[0].id,
variant_title: product.variants[0].title,
vendor: product.vendor,
total_discount: 0,
final_price: product.price_min,
product_type: product.type,
quantity: 1
}
});
self.ecommerceDataLayer('search', {search_term, items});
})
}else {
self.ecommerceDataLayer('search', {search_term, items: []});
}
});
});
}
else if(requestURL.includes("/cart/add")) {
const data = JSON.parse(xhr.responseText);
if(data.items && Array.isArray(data.items)) {
data.items.forEach(function(item) {
self.ecommerceDataLayer('add_to_cart', {items: [item]});
})
} else {
self.ecommerceDataLayer('add_to_cart', {items: [data]});
}
self.updateCart();
}else if(requestURL.includes("/cart/change")) {
const newCart = JSON.parse(xhr.responseText);
const newCartItems = newCart.items;
let oldCartItems = self.cart.items;
for(let i = 0; i < oldCartItems.length; i++) {
let item = oldCartItems[i];
let newItem = newCartItems.find(newItems => newItems.id === item.id);
if(newItem) {
if(newItem.quantity > item.quantity) {
// cart item increment
let quantity = (newItem.quantity - item.quantity);
let updatedItem = {...item, quantity}
self.ecommerceDataLayer('add_to_cart', {items: [updatedItem]});
self.updateCart();
}else if(newItem.quantity < item.quantity) {
// cart item decrement
let quantity = (item.quantity - newItem.quantity);
let updatedItem = {...item, quantity}
self.ecommerceDataLayer('remove_from_cart', {items: [updatedItem]});
self.updateCart();
}
}else {
self.ecommerceDataLayer('remove_from_cart', {items: [item]});
self.updateCart();
}
}
}
}
}
});
}
return origSend.apply(this, arguments);
};
return xhr;
};
//end xhr
}
// search event from search page
searchPageData() {
const self = this;
let pageUrl = window.location.href;
if(/.+\/search\?.*\&?q=.+/.test(pageUrl)) {
const queryString = pageUrl.split('?')[1];
const urlParams = new URLSearchParams(queryString);
const search_term = urlParams.get("q");
fetch(`{{ shop.secure_url }}/search/suggest.json?q=${search_term}&resources[type]=product&requestFrom=uldt`)
.then(res => res.json())
.then(function(data) {
const products = data.resources.results.products;
if(products.length) {
const fetchRequests = products.map(product =>
fetch(`${self.storeURL}/${product.url.split('?')[0]}.js`)
.then(response => response.json())
.catch(error => console.error('Error fetching:', error))
);
Promise.all(fetchRequests)
.then(products => {
const items = products.map((product) => {
return {
product_id: product.id,
product_title: product.title,
variant_id: product.variants[0].id,
variant_title: product.variants[0].title,
vendor: product.vendor,
total_discount: 0,
final_price: product.price_min,
product_type: product.type,
quantity: 1
}
});
self.ecommerceDataLayer('search', {search_term, items});
});
}else {
self.ecommerceDataLayer('search', {search_term, items: []});
}
});
}
}
// view_cart
miniCartData() {
if(this.miniCartButton.length) {
let self = this;
if(this.miniCartAppersOn === 'hover') {
this.miniCartAppersOn = 'mouseenter';
}
this.miniCartButton.forEach((selector) => {
let miniCartButtons = document.querySelectorAll(selector);
miniCartButtons.forEach((miniCartButton) => {
miniCartButton.addEventListener(self.miniCartAppersOn, () => {
self.ecommerceDataLayer('view_cart', self.cart);
});
})
});
}
}
// begin_checkout
beginCheckoutData() {
let self = this;
document.addEventListener('pointerdown', (event) => {
let targetElement = event.target.closest(self.beginCheckoutButtons.join(', '));
if(targetElement) {
self.ecommerceDataLayer('begin_checkout', self.cart);
}
});
}
// view_cart, add_to_cart, remove_from_cart
viewCartPageData() {
this.ecommerceDataLayer('view_cart', this.cart);
//if cart quantity chagne reload page
if(!this.isAjaxCartIncrementDecrement) {
const self = this;
document.addEventListener('pointerdown', (event) => {
const target = event.target.closest('a[href*="/cart/change?"]');
if(target) {
const linkUrl = target.getAttribute('href');
const queryString = linkUrl.split("?")[1];
const urlParams = new URLSearchParams(queryString);
const newQuantity = urlParams.get("quantity");
const line = urlParams.get("line");
const cart_id = urlParams.get("id");
if(newQuantity && (line || cart_id)) {
let item = line ? {...self.cart.items[line - 1]} : self.cart.items.find(item => item.key === cart_id);
let event = 'add_to_cart';
if(newQuantity < item.quantity) {
event = 'remove_from_cart';
}
let quantity = Math.abs(newQuantity - item.quantity);
item['quantity'] = quantity;
self.ecommerceDataLayer(event, {items: [item]});
}
}
});
}
}
productSinglePage() {
{% if template contains 'product' %}
const item = {
product_id: {{ product.id | json }},
variant_id: {{ product.selected_or_first_available_variant.id }},
product_title: {{ product.title | json }},
line_level_total_discount: 0,
vendor: {{ product.vendor | json }},
sku: {{ product.selected_or_first_available_variant.sku | json }},
product_type: {{ product.type | json }},
item_list_id: {{ product.collections[0].id | json }},
item_list_name: {{ product.collections[0].title | json }},
{% if product.selected_or_first_available_variant.title != "Default Title" %}
variant_title: {{ product.selected_or_first_available_variant.title | json }},
{% endif %}
final_price: {{ product.selected_or_first_available_variant.price }},
quantity: 1
};
const variants = {{ product.variants | json }}
this.ecommerceDataLayer('view_item', {items: [item]});
if(this.shopifyDirectCheckoutButton.length) {
let self = this;
document.addEventListener('pointerdown', (event) => {
let target = event.target;
let checkoutButton = event.target.closest(this.shopifyDirectCheckoutButton.join(', '));
if(checkoutButton && (variants || self.quickViewVariants)) {
let checkoutForm = checkoutButton.closest('form[action*="/cart/add"]');
if(checkoutForm) {
let variant_id = null;
let varientInput = checkoutForm.querySelector('input[name="id"]');
let varientIdFromURL = new URLSearchParams(window.location.search).get('variant');
let firstVarientId = item.variant_id;
if(varientInput) {
variant_id = parseInt(varientInput.value);
}else if(varientIdFromURL) {
variant_id = varientIdFromURL;
}else if(firstVarientId) {
variant_id = firstVarientId;
}
if(variant_id) {
variant_id = parseInt(variant_id);
let quantity = 1;
let quantitySelector = checkoutForm.getAttribute('id');
if(quantitySelector) {
let quentityInput = document.querySelector('input[name="quantity"][form="'+quantitySelector+'"]');
if(quentityInput) {
quantity = +quentityInput.value;
}
}
if(variant_id) {
let variant = variants.find(item => item.id === +variant_id);
if(variant && item) {
variant_id
item['variant_id'] = variant_id;
item['variant_title'] = variant.title;
item['final_price'] = variant.price;
item['quantity'] = quantity;
self.ecommerceDataLayer('add_to_cart', {items: [item]});
self.ecommerceDataLayer('begin_checkout', {items: [item]});
}else if(self.quickViewedItem) {
let variant = self.quickViewVariants.find(item => item.id === +variant_id);
if(variant) {
self.quickViewedItem['variant_id'] = variant_id;
self.quickViewedItem['variant_title'] = variant.title;
self.quickViewedItem['final_price'] = parseFloat(variant.price) * 100;
self.quickViewedItem['quantity'] = quantity;
self.ecommerceDataLayer('add_to_cart', {items: [self.quickViewedItem]});
self.ecommerceDataLayer('begin_checkout', {items: [self.quickViewedItem]});
}
}
}
}
}
}
});
}
{% endif %}
}
collectionsPageData() {
var ecommerce = {
'items': [
{% for product in collection.products %}
{
'product_id': {{ product.id | json }},
'variant_id': {{ product.selected_or_first_available_variant.id | json }},
'vendor': {{ product.vendor | json }},
'sku': {{ product.selected_or_first_available_variant.sku | json }},
'total_discount': 0,
'variant_title': {{ product.selected_or_first_available_variant.title | json }},
'product_title': {{ product.title | json }},
'final_price': Number({{ product.price }}),
'product_type': {{ product.type | json }},
'item_list_id': {{ collection.id | json }},
'item_list_name': {{ collection.title | json }},
'quantity': 1
},
{% endfor %}
]
};
ecommerce['item_list_id'] = {{ collection.id | json }}
ecommerce['item_list_name'] = {{ collection.title | json }}
this.ecommerceDataLayer('view_item_list', ecommerce);
}
// add to wishlist
addToWishListData() {
if(this.addToWishListSelectors && this.addToWishListSelectors.addWishListIcon) {
const self = this;
document.addEventListener('pointerdown', (event) => {
let target = event.target;
if(target.closest(self.addToWishListSelectors.addWishListIcon)) {
let pageULR = window.location.href.replace(/\?.+/, '');
let requestURL = undefined;
if(/\/products\/[^/]+$/.test(pageULR)) {
requestURL = pageULR;
} else if(self.addToWishListSelectors.gridItemSelector && self.addToWishListSelectors.productLinkSelector) {
let itemElement = target.closest(self.addToWishListSelectors.gridItemSelector);
if(itemElement) {
let linkElement = itemElement.querySelector(self.addToWishListSelectors.productLinkSelector);
if(linkElement) {
let link = linkElement.getAttribute('href').replace(/\?.+/g, '');
if(link && /\/products\/[^/]+$/.test(link)) {
requestURL = link;
}
}
}
}
if(requestURL) {
fetch(requestURL + '.json')
.then(res => res.json())
.then(result => {
let data = result.product;
if(data) {
let dataLayerData = {
product_id: data.id,
variant_id: data.variants[0].id,
product_title: data.title,
quantity: 1,
final_price: parseFloat(data.variants[0].price) * 100,
total_discount: 0,
product_type: data.product_type,
vendor: data.vendor,
variant_title: (data.variants[0].title !== 'Default Title') ? data.variants[0].title : undefined,
sku: data.variants[0].sku,
}
self.ecommerceDataLayer('add_to_wishlist', {items: [dataLayerData]});
}
});
}
}
});
}
}
quickViewData() {
if(this.quickViewSelector.quickViewElement && this.quickViewSelector.gridItemSelector && this.quickViewSelector.productLinkSelector) {
const self = this;
document.addEventListener('pointerdown', (event) => {
let target = event.target;
if(target.closest(self.quickViewSelector.quickViewElement)) {
let requestURL = undefined;
let itemElement = target.closest(this.quickViewSelector.gridItemSelector );
if(itemElement) {
let linkElement = itemElement.querySelector(self.quickViewSelector.productLinkSelector);
if(linkElement) {
let link = linkElement.getAttribute('href').replace(/\?.+/g, '');
if(link && /\/products\/[^/]+$/.test(link)) {
requestURL = link;
}
}
}
if(requestURL) {
fetch(requestURL + '.json')
.then(res => res.json())
.then(result => {
let data = result.product;
if(data) {
let dataLayerData = {
product_id: data.id,
variant_id: data.variants[0].id,
product_title: data.title,
quantity: 1,
final_price: parseFloat(data.variants[0].price) * 100,
total_discount: 0,
product_type: data.product_type,
vendor: data.vendor,
variant_title: (data.variants[0].title !== 'Default Title') ? data.variants[0].title : undefined,
sku: data.variants[0].sku,
}
self.ecommerceDataLayer('view_item', {items: [dataLayerData]});
self.quickViewVariants = data.variants;
self.quickViewedItem = dataLayerData;
}
});
}
}
});
{% unless template contains 'product' %}
if(this.shopifyDirectCheckoutButton.length) {
let self = this;
document.addEventListener('pointerdown', (event) => {
let target = event.target;
let checkoutButton = event.target.closest(this.shopifyDirectCheckoutButton.join(', '));
if(self.quickViewVariants && self.quickViewedItem && self.quickViewVariants.length && checkoutButton) {
let checkoutForm = checkoutButton.closest('form[action*="/cart/add"]');
if(checkoutForm) {
let quantity = 1;
let varientInput = checkoutForm.querySelector('input[name="id"]');
let quantitySelector = checkoutForm.getAttribute('id');
if(quantitySelector) {
let quentityInput = document.querySelector('input[name="quantity"][form="'+quantitySelector+'"]');
if(quentityInput) {
quantity = +quentityInput.value;
}
}
if(varientInput) {
let variant_id = parseInt(varientInput.value);
if(variant_id) {
const variant = self.quickViewVariants.find(item => item.id === +variant_id);
if(variant && self.quickViewedItem) {
self.quickViewedItem['variant_id'] = variant_id;
self.quickViewedItem['variant_title'] = variant.title;
self.quickViewedItem['final_price'] = parseFloat(variant.price) * 100;
self.quickViewedItem['quantity'] = quantity;
self.ecommerceDataLayer('add_to_cart', {items: [self.quickViewedItem]});
self.ecommerceDataLayer('begin_checkout', {items: [self.quickViewedItem]});
}
}
}
}
}
});
}
{% endunless %}
}
}
// all ecommerce events
ecommerceDataLayer(event, data) {
const self = this;
dataLayer.push({ 'ecommerce': null });
const dataLayerData = {
"event": this.eventPrefix + event,
'ecommerce': {
'currency': this.cart.currency,
'items': data.items.map((item, index) => {
const dataLayerItem = {
'index': index,
'item_id': this.formattedItemId ? `shopify_${this.countryCode}_${item.product_id}_${item.variant_id}` : item.product_id.toString(),
'product_id': item.product_id.toString(),
'variant_id': item.variant_id.toString(),
'item_name': item.product_title,
'quantity': item.quantity,
'price': +((item.final_price / 100).toFixed(2)),
'discount': item.total_discount ? +((item.total_discount / 100).toFixed(2)) : 0
}
if(item.product_type) {
dataLayerItem['item_category'] = item.product_type;
}
if(item.vendor) {
dataLayerItem['item_brand'] = item.vendor;
}
if(item.variant_title && item.variant_title !== 'Default Title') {
dataLayerItem['item_variant'] = item.variant_title;
}
if(item.sku) {
dataLayerItem['sku'] = item.sku;
}
if(item.item_list_name) {
dataLayerItem['item_list_name'] = item.item_list_name;
}
if(item.item_list_id) {
dataLayerItem['item_list_id'] = item.item_list_id.toString()
}
return dataLayerItem;
})
}
}
if(data.total_price !== undefined) {
dataLayerData['ecommerce']['value'] = +((data.total_price / 100).toFixed(2));
} else {
dataLayerData['ecommerce']['value'] = +(dataLayerData['ecommerce']['items'].reduce((total, item) => total + (item.price * item.quantity), 0)).toFixed(2);
}
if(data.item_list_id) {
dataLayerData['ecommerce']['item_list_id'] = data.item_list_id;
}
if(data.item_list_name) {
dataLayerData['ecommerce']['item_list_name'] = data.item_list_name;
}
if(data.search_term) {
dataLayerData['search_term'] = data.search_term;
}
if(self.dataSchema.dynamicRemarketing && self.dataSchema.dynamicRemarketing.show) {
dataLayer.push({ 'dynamicRemarketing': null });
dataLayerData['dynamicRemarketing'] = {
value: dataLayerData.ecommerce.value,
items: dataLayerData.ecommerce.items.map(item => ({id: item.item_id, google_business_vertical: self.dataSchema.dynamicRemarketing.business_vertical}))
}
}
if(!self.dataSchema.ecommerce || !self.dataSchema.ecommerce.show) {
delete dataLayerData['ecommerce'];
}
dataLayer.push(dataLayerData);
self.eventConsole(self.eventPrefix + event, dataLayerData);
}
// contact form submit & newsletters signup
formData() {
const self = this;
document.addEventListener('submit', function(event) {
let targetForm = event.target.closest('form[action^="/contact"]');
if(targetForm) {
const formData = {
form_location: window.location.href,
form_id: targetForm.getAttribute('id'),
form_classes: targetForm.getAttribute('class')
};
let formType = targetForm.querySelector('input[name="form_type"]');
let inputs = targetForm.querySelectorAll("input:not([type=hidden]):not([type=submit]), textarea, select");
inputs.forEach(function(input) {
var inputName = input.name;
var inputValue = input.value;
if (inputName && inputValue) {
var matches = inputName.match(/\[(.*?)\]/);
if (matches && matches.length > 1) {
var fieldName = matches[1];
formData[fieldName] = input.value;
}
}
});
if(formType && formType.value === 'customer') {
dataLayer.push({ event: self.eventPrefix + 'newsletter_signup', ...formData});
self.eventConsole(self.eventPrefix + 'newsletter_signup', { event: self.eventPrefix + 'newsletter_signup', ...formData});
} else if(formType && formType.value === 'contact') {
dataLayer.push({ event: self.eventPrefix + 'contact_form_submit', ...formData});
self.eventConsole(self.eventPrefix + 'contact_form_submit', { event: self.eventPrefix + 'contact_form_submit', ...formData});
}
}
});
}
// phone_number_click event
phoneClickData() {
const self = this;
document.addEventListener('click', function(event) {
let target = event.target.closest('a[href^="tel:"]');
if(target) {
let phone_number = target.getAttribute('href').replace('tel:', '');
let eventData = {
event: self.eventPrefix + 'phone_number_click',
page_location: window.location.href,
link_classes: target.getAttribute('class'),
link_id: target.getAttribute('id'),
phone_number
}
dataLayer.push(eventData);
this.eventConsole(self.eventPrefix + 'phone_number_click', eventData);
}
});
}
// email_click event
emailClickData() {
const self = this;
document.addEventListener('click', function(event) {
let target = event.target.closest('a[href^="mailto:"]');
if(target) {
let email_address = target.getAttribute('href').replace('mailto:', '');
let eventData = {
event: self.eventPrefix + 'email_click',
page_location: window.location.href,
link_classes: target.getAttribute('class'),
link_id: target.getAttribute('id'),
email_address
}
dataLayer.push(eventData);
this.eventConsole(self.eventPrefix + 'email_click', eventData);
}
});
}
}
// end Ultimate_Shopify_DataLayer
document.addEventListener('DOMContentLoaded', function() {
try{
new Ultimate_Shopify_DataLayer();
}catch(error) {
console.log(error);
}
});
})();
</script>
checkout.liquid
<!-- Google Tag Manager -->
<script>
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != "dataLayer" ? "&l=" + l : "";
j.async = true;
j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, "script", "dataLayer", "GTM-00000000");
</script>
<!-- End Google Tag Manager -->
{% if first_time_accessed %}
<!-- GTM DataLayer -->
<script>
(function() {
window.dataLayer = window.dataLayer || [];
const eventPrefix = '';
const formatedItemId = true;
const dataSchema = {
ecommerce: {
show: true
},
dynamicRemarketing: {
show: false,
business_vertical: 'retail'
}
}
// customer information
const currentUser = {};
{% if customer %}
currentUser.id = {{ customer.id }};
currentUser.first_name = "{{ customer.first_name }}";
currentUser.last_name = "{{ customer.last_name }}";
currentUser.full_name = "{{ customer.name }}";
currentUser.email = "{{ customer.email }}";
currentUser.phone = "{{ customer.default_address.phone }}";
currentUser.new_customer = {% if customer.orders_count < 2 %}true{% else %}false{% endif %};
{% if customer.default_address %}
currentUser.address = {
address_summary: "{{ customer.default_address.summary }}",
address1: "{{ customer.default_address.address1 }}",
address2: "{{ customer.default_address.address2 }}",
city: "{{ customer.default_address.city }}",
street: "{{ customer.default_address.street }}",
zip: "{{ customer.default_address.zip }}",
company: "{{ customer.default_address.company }}",
country: "{{ customer.default_address.country.name }}",
countryCode: "{{ customer.default_address.country_code }}",
province: "{{ customer.default_address.province }}"
};
{% endif %}
{% elsif checkout.customer %}
currentUser.id = {{ checkout.customer.id }};
currentUser.first_name = "{{ checkout.customer.first_name }}";
currentUser.last_name = "{{ checkout.customer.last_name }}";
currentUser.full_name = "{{ checkout.customer.name }}";
currentUser.email = "{{ checkout.customer.email }}";
currentUser.phone = "{{ checkout.customer.default_address.phone }}";
currentUser.new_customer = {% if checkout.customer.orders_count < 2 %}true{% else %}false{% endif %};
{% if checkout.billing_address %}
currentUser.address = {
address_summary: "{{ checkout.billing_address.summary }}",
address1: "{{ checkout.billing_address.address1 }}",
address2: "{{ checkout.billing_address.address2 }}",
city: "{{ checkout.billing_address.city }}",
street: "{{ checkout.billing_address.street }}",
zip: "{{ checkout.billing_address.zip }}",
company: "{{ checkout.billing_address.company }}",
country: "{{ checkout.billing_address.country.name }}",
countryCode: "{{ checkout.billing_address.country_code }}",
province: "{{ checkout.billing_address.province }}"
};
{% endif %}
{% endif %}
if (currentUser.email) {
currentUser.hash_email = "{{ currentUser.email | sha256}}"
}
if (currentUser.phone) {
currentUser.hash_phone = "{{ currentUser.phone | sha256}}"
}
dataLayer.push({
customer: currentUser
});
// purchase event
const couponCode = [];
{% for discount_application in discount_applications %}
couponCode.push("{{ discount_application.title }}")
{% endfor %}
dataLayer.push({ ecommerce: null });
const dataLayerData = {
event: eventPrefix + "purchase",
ecommerce: {
transaction_id: "{{ checkout.order_number | replace: '#', '' | default: checkout.id | replace: '#', '' }}",
value: {{ checkout.total_price | times: 0.01 }},
tax: {{ checkout.tax_price | times: 0.01 }},
shipping: {{ checkout.shipping_price | times: 0.01 }},
discount: {{ checkout.discounts_amount | times: 0.01 }},
currency: "{{ checkout.currency }}",
coupon: couponCode.join(','),
items: [
{% for line_item in line_items %}
{
item_id: formatedItemId ? "shopify_{{ shop.address.country_code }}_{{ line_item.product_id }}_{{ line_item.variant.id }}" : "{{ line_item.product_id }}",
product_id: "{{ line_item.product_id }}",
variant_id: "{{ line_item.variant.id }}",
item_name: "{{ line_item.product.title }}",
{% if line_item.variant.title != "Default Title" %}
'item_variant': "{{ line_item.variant.title }}",
{% endif %}
discount: {{ line_item.line_level_total_discount | times: 0.01 }},
sku: "{{ line_item.sku }}",
price: {{ line_item.final_price | times: 0.01 }},
quantity: {{ line_item.quantity }},
item_brand: "{{ line_item.vendor }}",
item_category: "{{ line_item.product.type }}"
},
{% endfor %}
]
}
}
if(dataSchema.dynamicRemarketing && dataSchema.dynamicRemarketing.show) {
dataLayerData['dynamicRemarketing'] = {
value: dataLayerData.ecommerce.value,
items: dataLayerData.ecommerce.items.map(item => ({id: item.item_id, google_business_vertical: dataSchema.dynamicRemarketing.business_vertical}))
}
}
if(!dataSchema.ecommerce || !dataSchema.ecommerce.show) {
delete dataLayerData['ecommerce'];
}
dataLayer.push(dataLayerData);
})();
</script>
<!-- END GTM DataLayer -->
{% endif %}
How to Setup Shopify DataLayer?
- Adding GTM Tracking Code:
- Access your Shopify Store Dashboard.
- Navigate to
Online Store > Themes > Edit code (3 dots on the right of the active theme)
and find thetheme.liquid
file. - Paste the GTM tracking code right after the
<head>
tag in thetheme.liquid
file.
- 2. Adding Ultimate Datalayer:
- Inside the
snippets
folder, create a new file namedultimate-datalayer.liquid
. - Copy all the code from the
ultimate-datalayer.liquid
shown at the top of the provided instructions and paste it into the newly createdultimate-datalayer.liquid
file.
- Inside the
- Including Ultimate Datalayer in Theme File:
- In the
theme.liquid
file, after your GTM tracking code, include theultimate-datalayer.liquid
file by adding{% render 'ultimate-datalayer' %}
- In the
- Adding Code to Checkout Settings:
- Go to Shopify admin dashboard > Settings > Checkout (Scroll Down) > Additional scripts.
- Copy the code from
checkout.liquid
shown at the top of the provided instructions. - Paste this code into the “Additional scripts” field.
- Replace the example GTM tracking ID
GTM-00000
with your actual GTM container ID.
Remember, depending on your specific Shopify theme, you might need to make some adjustments to these steps. If needed, refer to additional resources or a video guide for more detailed information on this process.
Source Code
Hi Hassan, really a good script especially since the values for new_customer are provided. Would it also be possible to include the customer lifetime value here?
Many thanks
It is possible for only for logged customer.
Hi, I’m excited about your script. But I would be interested to know where you got the information for customer.orders_count which you use to determine the new_customer?
Because with this information it would also be possible to determine the customer.total.value.
Do you have any ideas on how to adapt the script for this?
If customer is logged in then we can count customer’s orders.
Yes, we can.
et vero quas rem fugiat placeat saepe dignissimos aut. quia omnis enim ut maxime ipsam ex provident. repellendus et libero ut quia qui atque voluptas corporis officiis qui pariatur aut quidem ut nesci
Just wow!!! Anybody is docile to love it. 💝💝💝
Thanks Rajib
Hi. Thanks. How to change the product id for sku?
My merchant center uses sku.
Thank you for this, Hassan. Question, the code you provided can be used for any Shopify? or does it have to be generated from our Google Analytics account?
Yes, this code should work for any Shopify website weather it is normal Shopify or Shopify Plus
I’ve learned a lot about email marketing metrics from your blog.
Happy to know.
You can check our youtube channel for more videos this Google Analytics and tracking: https://youtube.com/@leocoder
The tips for using social proof in emails were great.
We are not clear about what you mean here. Can you please share more details so that we can help you?
Hello Hassan,
It’s really impressive what a script you’ve built here. I have the script in use and it works perfectly. Now I have seen that you also provide the script for Webpixel. However, I am missing the datalayer for new_customer. Are you planning to develop the script further?
Can you please send an email with details about info@leomeasure.com? I hope we can assist you.
Utilize these resources to enhance your website’s SEO performance effectively.
Thank you so much for your suggestion. If you need any help you can contact us.
there is a big issue in this code which is duplicate the puchase event, the add_to_cart etc
For duplicate events, one of the events is coming from the Shopify app. We recommend using the event prefix feature to resolve the problem.
Event prefix details: https://youtu.be/98w-kEKLICQ?t=1226
I cannot see add_shipping_info and add_payment_info in the datalayer 🙁
For this, you need to use Shopify customer pixel instead of additional scripts on the checkout page.
Screenshot: https://prnt.sc/vVWuNviM_GEF
If you need further details you can contact us.
Does this work with Shopify’s Checkout Extensibility update?
Yes, you need to check GitHub and use customer pixel instead of additional scripts on checkout
Screenshot: https://prnt.sc/vVWuNviM_GEF
If you need further details you can contact us.