Shopify Data Layer Structure
Shopify provides a native data layer that automatically populates with page, product, cart, and customer data. This data layer integrates seamlessly with Google Tag Manager (GTM).
Data Layer Overview
Shopify pushes events to window.dataLayer using standard ecommerce data layer format. GTM can access this data through variables and triggers.
How Shopify Data Layer Works
- Page loads → Shopify theme pushes initial data
- User interactions → Events pushed to data layer
- GTM listens → Captures events and variables
- Tags fire → Send data to analytics platforms
Global Data Layer Object
On every page load, Shopify pushes a base data layer object:
window.dataLayer = [{
event: 'page_viewed',
page: {
pageType: 'home', // home, collection, product, cart, etc.
resourceType: 'home',
resourceId: null
},
user: {
customerId: null, // null for guests, ID for logged-in
customerEmail: null,
customerFirstName: null,
customerLastName: null,
customerPhone: null,
customerOrdersCount: 0,
customerTotalSpent: '0.00'
},
cart: {
currency: 'USD',
totalPrice: 0,
itemCount: 0
}
}];
Page Types
The page.pageType value indicates the current page:
| Page Type | Description | Example URL |
|---|---|---|
home |
Homepage | / |
collection |
Collection/category page | /collections/all |
product |
Product detail page | /products/example-product |
cart |
Shopping cart | /cart |
search |
Search results | /search?q=query |
page |
Static page | /pages/about |
blog |
Blog listing | /blogs/news |
article |
Blog post | /blogs/news/article-title |
404 |
Not found | Any invalid URL |
password |
Password page | Pre-launch password page |
Accessing Page Type in GTM
Create Variable:
- Type: Data Layer Variable
- Data Layer Variable Name:
page.pageType - Name:
DLV - Page Type
Use in Triggers:
- Fire tags only on specific page types
- Example: Fire product tag only when
page.pageTypeequalsproduct
Shopify Data Layer Events
Shopify automatically fires these events:
1. page_viewed
Fires on every page load.
{
event: 'page_viewed',
page: {
pageType: 'product',
resourceType: 'product',
resourceId: 1234567890
},
user: { /* user data */ },
cart: { /* cart data */ }
}
GTM Trigger:
- Type: Custom Event
- Event name:
page_viewed
2. product_viewed
Fires when customer views a product detail page.
{
event: 'product_viewed',
ecommerce: {
currencyCode: 'USD',
detail: {
products: [{
id: 1234567890,
name: 'Example Product',
price: '29.99',
brand: 'Brand Name',
category: 'Category Name',
variant: 'Medium / Blue',
sku: 'SKU-123'
}]
}
}
}
GTM Variable (Product ID):
- Type: Data Layer Variable
- Data Layer Variable Name:
ecommerce.detail.products.0.id - Name:
DLV - Product ID
GTM Variable (Product Name):
- Type: Data Layer Variable
- Data Layer Variable Name:
ecommerce.detail.products.0.name - Name:
DLV - Product Name
3. collection_viewed
Fires on collection/category pages.
{
event: 'collection_viewed',
ecommerce: {
currencyCode: 'USD',
actionField: {
list: 'Collection Name'
},
impressions: [{
id: 1234567890,
name: 'Product 1',
price: '19.99',
brand: 'Brand',
category: 'Category',
position: 1
}, {
id: 1234567891,
name: 'Product 2',
price: '24.99',
brand: 'Brand',
category: 'Category',
position: 2
}]
}
}
Note: The impressions array may contain up to 50 products.
4. search_submitted
Fires when customer performs a search.
{
event: 'search_submitted',
search: {
searchTerm: 'customer query'
}
}
GTM Variable:
- Type: Data Layer Variable
- Data Layer Variable Name:
search.searchTerm - Name:
DLV - Search Term
5. product_added_to_cart
Fires when item is added to cart.
{
event: 'product_added_to_cart',
ecommerce: {
currencyCode: 'USD',
add: {
products: [{
id: 1234567890,
name: 'Example Product',
price: '29.99',
brand: 'Brand Name',
category: 'Category',
variant: 'Medium / Blue',
quantity: 1
}]
}
}
}
GTM Variable (Added Product):
// Custom JavaScript Variable
function() {
const dl = window.dataLayer || [];
for (let i = dl.length - 1; i >= 0; i--) {
if (dl[i].ecommerce && dl[i].ecommerce.add) {
return dl[i].ecommerce.add.products;
}
}
return [];
}
6. product_removed_from_cart
Fires when item is removed from cart.
{
event: 'product_removed_from_cart',
ecommerce: {
currencyCode: 'USD',
remove: {
products: [{
id: 1234567890,
name: 'Example Product',
price: '29.99',
quantity: 1
}]
}
}
}
7. checkout_started (Shopify Plus Only)
Fires when checkout begins (only on Plus stores in checkout.liquid).
{
event: 'checkout_started',
ecommerce: {
currencyCode: 'USD',
checkout: {
actionField: {
step: 1
},
products: [{
id: 1234567890,
name: 'Example Product',
price: '29.99',
quantity: 1
}]
}
}
}
8. checkout_completed
Fires on order confirmation page (all stores).
{
event: 'checkout_completed',
ecommerce: {
currencyCode: 'USD',
purchase: {
actionField: {
id: 'ORDER-123',
affiliation: 'Shop Name',
revenue: '59.98',
tax: '5.00',
shipping: '10.00'
},
products: [{
id: 1234567890,
name: 'Example Product',
price: '29.99',
quantity: 2
}]
}
}
}
User Data
Customer information available in data layer (when logged in):
{
user: {
customerId: 1234567890,
customerEmail: 'customer@example.com',
customerFirstName: 'John',
customerLastName: 'Doe',
customerPhone: '+15551234567',
customerOrdersCount: 5,
customerTotalSpent: '299.95',
customerTags: ['VIP', 'Newsletter']
}
}
GTM Variables:
Customer ID:
- Data Layer Variable Name:
user.customerId - Name:
DLV - Customer ID
Customer Email (hashed recommended):
- Data Layer Variable Name:
user.customerEmail - Name:
DLV - Customer Email
Customer Lifetime Value:
- Data Layer Variable Name:
user.customerTotalSpent - Name:
DLV - Customer LTV
Cart Data
Current cart information:
{
cart: {
currency: 'USD',
totalPrice: 89.97,
itemCount: 3,
items: [{
productId: 1234567890,
variantId: 9876543210,
name: 'Example Product',
price: 29.99,
quantity: 3
}]
}
}
GTM Variables:
Cart Total:
- Data Layer Variable Name:
cart.totalPrice - Name:
DLV - Cart Total
Cart Item Count:
- Data Layer Variable Name:
cart.itemCount - Name:
DLV - Cart Item Count
Creating GTM Variables
Method 1: Data Layer Variables (Simple)
For simple values that exist directly in data layer:
- Variables → New
- Variable Type: Data Layer Variable
- Data Layer Variable Name: Enter the path (e.g.,
page.pageType) - Data Layer Version: Version 2
- Name: Give it a descriptive name (e.g.,
DLV - Page Type)
Method 2: Custom JavaScript (Complex)
For values that need processing or extraction:
// Variable: Get Product Items Array for GA4
function() {
const dl = window.dataLayer || [];
// Find most recent product_viewed event
for (let i = dl.length - 1; i >= 0; i--) {
if (dl[i].ecommerce && dl[i].ecommerce.detail) {
const products = dl[i].ecommerce.detail.products;
// Transform to GA4 format
return products.map(function(p) {
return {
item_id: p.id,
item_name: p.name,
item_brand: p.brand || '',
item_category: p.category || '',
item_variant: p.variant || '',
price: parseFloat(p.price),
quantity: 1
};
});
}
}
return [];
}
Method 3: Lookup Tables
For mapping Shopify values to custom values:
- Variable Type: Lookup Table
- Input Variable:
\{\{DLV - Page Type\}\} - Mappings:
product→Product Detailcollection→Categorycart→Shopping Cart
- Default Value:
Other
Common GTM Triggers for Shopify
All Pages Trigger
Product Page Trigger
- Type: Page View - DOM Ready
- Condition:
Page Type equals product - Use for: Product-specific tags
Product Viewed Event Trigger
- Type: Custom Event
- Event name:
product_viewed - Use for: GA4 view_item event
Add to Cart Trigger
- Type: Custom Event
- Event name:
product_added_to_cart - Use for: GA4 add_to_cart, Meta Pixel AddToCart
Purchase Trigger
- Type: Custom Event
- Event name:
checkout_completed - Use for: GA4 purchase, Meta Pixel Purchase
Theme-Specific Variations
Dawn Theme (Shopify 2.0)
Dawn and OS 2.0 themes have full data layer support out of the box.
Older Themes (Legacy)
Some older themes may have:
- Incomplete data layer implementation
- Different variable names
- Missing events
Fix: Update theme or manually implement missing events.
Custom Themes
If your theme doesn't populate the data layer:
// Add to theme.liquid to manually push product data
{% if template.name == 'product' %}
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'product_viewed',
ecommerce: {
currencyCode: '{{ cart.currency.iso_code }}',
detail: {
products: [{
id: '{{ product.id }}',
name: '{{ product.title | escape }}',
price: '{{ product.selected_or_first_available_variant.price | money_without_currency }}',
brand: '{{ product.vendor | escape }}',
category: '{{ product.type | escape }}',
variant: '{{ product.selected_or_first_available_variant.title | escape }}'
}]
}
}
});
</script>
{% endif %}
Debugging Data Layer
Console Commands
View entire data layer:
console.table(window.dataLayer);
Find specific event:
window.dataLayer.filter(obj => obj.event === 'product_viewed');
Monitor new pushes:
const originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
console.log('New Data Layer Push:', arguments[0]);
originalPush.apply(window.dataLayer, arguments);
};
GTM Preview Mode
- Enable Preview in GTM
- Navigate to your Shopify store
- Click Data Layer tab in Tag Assistant
- Inspect each data layer push
- Verify values populate correctly
Best Practices
1. Don't Modify Shopify's Native Events
Shopify's data layer is automatically populated. Avoid overwriting these events unless necessary.
2. Add Custom Events Carefully
If adding custom events, use unique event names:
// Good
dataLayer.push({event: 'custom_newsletter_signup'});
// Bad (conflicts with Shopify)
dataLayer.push({event: 'product_viewed'});
3. Handle Missing Data
Always provide fallback values:
// GTM Custom JavaScript Variable
function() {
return {{DLV - Product Brand}} || 'Unknown Brand';
}
4. Normalize Currency
Ensure currency values are numeric:
{{ price | money_without_currency }} // Good: 19.99
{{ price }} // Bad: $19.99
5. Respect Customer Privacy
Hash or remove PII before sending to third parties:
// Hash email before sending
function hashEmail(email) {
// Use SHA-256 or similar
return btoa(email); // Simple encoding (use proper hashing)
}
Troubleshooting
Data Layer is Empty
Check:
- GTM installed correctly in theme
- Modern theme with data layer support
- No JavaScript errors blocking execution
Variables Return Undefined
Check:
- Variable name matches exact data layer path
- Event has fired before variable is accessed
- Data layer version set to Version 2
Events Fire Multiple Times
Cause: Theme JavaScript or apps pushing duplicate events.
Fix: Debug with console to identify source, remove duplicates.
Next Steps
- GTM Setup - Install GTM on Shopify
- GA4 Event Tracking - Use data layer for GA4
- Events Not Firing - Debug tracking issues
For general data layer concepts, see Data Layer Guide.