Shopify GA4 Ecommerce Tracking | Blue Frog Docs

Shopify GA4 Ecommerce Tracking

Complete GA4 ecommerce tracking for Shopify including product views, add to cart, checkout, and purchase events.

Shopify GA4 Ecommerce Tracking

Implement comprehensive GA4 ecommerce tracking for your Shopify store, including product impressions, detail views, cart actions, checkout steps, and purchase transactions.

Ecommerce Events Overview

GA4 uses specific event names and parameters for ecommerce tracking. Here's how they map to Shopify:

Shopify Action GA4 Event Available On Plus Required
View collection view_item_list Collection pages No
View product view_item Product pages No
Add to cart add_to_cart All pages No
Remove from cart remove_from_cart Cart page No
View cart view_cart Cart page No
Begin checkout begin_checkout Checkout start Yes (Plus)
Add shipping info add_shipping_info Checkout shipping Yes (Plus)
Add payment info add_payment_info Checkout payment Yes (Plus)
Purchase purchase Order confirmation No

Product Impressions (Collection Pages)

Track when customers view product collections.

Using GTM + Shopify Data Layer

1. Create Data Layer Variable for Products

In GTM, create a Custom JavaScript variable:

function() {
  // Get products from Shopify collection data layer
  if (window.Shopify && window.Shopify.theme && window.Shopify.theme.collection) {
    return window.Shopify.theme.collection.products || [];
  }
  return [];
}

2. Create Trigger

  • Type: Page View - Window Loaded
  • Fire on: Page Type equals collection

3. Create GA4 Tag

  • Type: GA4 Event
  • Event Name: view_item_list
  • Parameters:
    • item_list_id: Collection ID
    • item_list_name: Collection name
    • items: Products array variable

Manual Implementation

Add to your collection template (collection.liquid or theme section):

<script>
  gtag('event', 'view_item_list', {
    item_list_id: '{{ collection.id }}',
    item_list_name: '{{ collection.title }}',
    items: [
      {% for product in collection.products limit: 20 %}
      {
        item_id: '{{ product.id }}',
        item_name: '{{ product.title }}',
        item_brand: '{{ product.vendor }}',
        item_category: '{{ product.type }}',
        item_list_name: '{{ collection.title }}',
        price: {{ product.price | money_without_currency }},
        index: {{ forloop.index }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>

Note: Limit to first 20 products to avoid data layer size issues.

Product Detail Views

Track when customers view individual products.

Using GTM + Shopify Data Layer

Shopify automatically fires product_viewed event. Create a GTM tag to translate it:

1. Create Trigger

  • Type: Custom Event
  • Event name: product_viewed

2. Create Variables

Extract product data from Shopify data layer:

// Variable: Product - Items Array
function() {
  const dl = window.dataLayer || [];
  for (let i = dl.length - 1; i >= 0; i--) {
    if (dl[i].ecommerce && dl[i].ecommerce.detail) {
      const products = dl[i].ecommerce.detail.products;
      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 [];
}

3. Create GA4 Tag

  • Type: GA4 Event
  • Event Name: view_item
  • Parameters:
    • currency: \{\{DLV - Currency\}\}
    • value: \{\{DLV - Product Price\}\}
    • items: \{\{Product - Items Array\}\}
  • Trigger: product_viewed

Manual Implementation

Add to product template (product.liquid or theme section):

<script>
  gtag('event', 'view_item', {
    currency: '{{ cart.currency.iso_code }}',
    value: {{ product.selected_or_first_available_variant.price | money_without_currency }},
    items: [{
      item_id: '{{ product.id }}',
      item_name: '{{ product.title | escape }}',
      item_brand: '{{ product.vendor | escape }}',
      item_category: '{{ product.type | escape }}',
      item_variant: '{{ product.selected_or_first_available_variant.title | escape }}',
      price: {{ product.selected_or_first_available_variant.price | money_without_currency }},
      quantity: 1
    }]
  });
</script>

Add to Cart

Track when items are added to cart.

Using GTM + Shopify Data Layer

Shopify fires product_added_to_cart automatically when customers add items.

1. Create Variables

// Variable: Add to Cart - Items
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.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: parseInt(p.quantity)
        };
      });
    }
  }
  return [];
}

// Variable: Add to Cart - Value
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.reduce(function(sum, p) {
        return sum + (parseFloat(p.price) * parseInt(p.quantity));
      }, 0);
    }
  }
  return 0;
}

2. Create GA4 Tag

  • Type: GA4 Event
  • Event Name: add_to_cart
  • Parameters:
    • currency: \{\{DLV - Currency\}\}
    • value: \{\{Add to Cart - Value\}\}
    • items: \{\{Add to Cart - Items\}\}
  • Trigger: Custom Event product_added_to_cart

Manual Theme Implementation

If your theme uses AJAX cart (most modern themes), listen for cart updates:

<script>
// Listen for Shopify AJAX cart add
document.addEventListener('DOMContentLoaded', function() {
  const addToCartForms = document.querySelectorAll('form[action*="/cart/add"]');

  addToCartForms.forEach(function(form) {
    form.addEventListener('submit', function(e) {
      // Get form data
      const formData = new FormData(form);
      const variantId = formData.get('id');
      const quantity = formData.get('quantity') || 1;

      // Fetch product data via AJAX
      fetch('/products/' + productHandle + '.js')
        .then(response => response.json())
        .then(product => {
          const variant = product.variants.find(v => v.id == variantId);

          gtag('event', 'add_to_cart', {
            currency: '{{ cart.currency.iso_code }}',
            value: (variant.price / 100) * quantity,
            items: [{
              item_id: product.id,
              item_name: product.title,
              item_brand: product.vendor,
              item_category: product.type,
              item_variant: variant.title,
              price: variant.price / 100,
              quantity: parseInt(quantity)
            }]
          });
        });
    });
  });
});
</script>

For themes with data attributes (like Dawn theme):

<script>
document.addEventListener('DOMContentLoaded', function() {
  // Listen for variant change to get current selection
  let currentVariant = {{ product.selected_or_first_available_variant | json }};

  const variantSelectors = document.querySelectorAll('[name="id"]');
  variantSelectors.forEach(function(selector) {
    selector.addEventListener('change', function() {
      const variantId = this.value;
      // Fetch variant data
      fetch('/variants/' + variantId + '.js')
        .then(response => response.json())
        .then(variant => {
          currentVariant = variant;
        });
    });
  });

  // Listen for add to cart
  const addToCartButtons = document.querySelectorAll('[name="add"]');
  addToCartButtons.forEach(function(button) {
    button.addEventListener('click', function(e) {
      const form = button.closest('form');
      const quantity = form.querySelector('[name="quantity"]').value || 1;

      gtag('event', 'add_to_cart', {
        currency: '{{ cart.currency.iso_code }}',
        value: (currentVariant.price / 100) * quantity,
        items: [{
          item_id: '{{ product.id }}',
          item_name: '{{ product.title | escape }}',
          item_brand: '{{ product.vendor | escape }}',
          item_category: '{{ product.type | escape }}',
          item_variant: currentVariant.title,
          price: currentVariant.price / 100,
          quantity: parseInt(quantity)
        }]
      });
    });
  });
});
</script>

Remove from Cart

Track cart item removals.

Using GTM

Similar to add to cart, Shopify fires product_removed_from_cart:

GA4 Tag:

  • Event Name: remove_from_cart
  • Parameters: Same as add to cart
  • Trigger: Custom Event product_removed_from_cart

Manual Implementation

For AJAX cart removals:

<script>
// Listen for cart updates (theme-specific)
document.addEventListener('cart:updated', function(event) {
  const removedItems = event.detail.removedItems || [];

  if (removedItems.length > 0) {
    gtag('event', 'remove_from_cart', {
      currency: '{{ cart.currency.iso_code }}',
      items: removedItems.map(item => ({
        item_id: item.product_id,
        item_name: item.title,
        price: item.price / 100,
        quantity: item.quantity
      }))
    });
  }
});
</script>

View Cart

Track when customers view their cart.

Implementation

Add to cart.liquid template:

<script>
  gtag('event', 'view_cart', {
    currency: '{{ cart.currency.iso_code }}',
    value: {{ cart.total_price | money_without_currency }},
    items: [
      {% for item in cart.items %}
      {
        item_id: '{{ item.product_id }}',
        item_name: '{{ item.product.title | escape }}',
        item_brand: '{{ item.vendor | escape }}',
        item_category: '{{ item.product.type | escape }}',
        item_variant: '{{ item.variant.title | escape }}',
        price: {{ item.final_price | money_without_currency }},
        quantity: {{ item.quantity }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>

Begin Checkout (Shopify Plus Only)

Track when checkout begins.

Shopify Plus: checkout.liquid

Add to checkout.liquid when customer reaches first checkout step:

{% if checkout.step == 'contact_information' and checkout.customer_email == blank %}
<script>
  gtag('event', 'begin_checkout', {
    currency: '{{ checkout.currency }}',
    value: {{ checkout.total_price | money_without_currency }},
    coupon: '{% if checkout.discount %}{{ checkout.discount.code }}{% endif %}',
    items: [
      {% for line_item in checkout.line_items %}
      {
        item_id: '{{ line_item.product_id }}',
        item_name: '{{ line_item.title | escape }}',
        item_brand: '{{ line_item.vendor | escape }}',
        item_category: '{{ line_item.product.type | escape }}',
        item_variant: '{{ line_item.variant_title | escape }}',
        price: {{ line_item.final_price | money_without_currency }},
        quantity: {{ line_item.quantity }},
        discount: {{ line_item.total_discount | money_without_currency }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>
{% endif %}

Non-Plus Alternative

Use cart page or when customer clicks "Checkout" button:

<script>
document.querySelector('button[name="checkout"]').addEventListener('click', function() {
  gtag('event', 'begin_checkout', {
    currency: '{{ cart.currency.iso_code }}',
    value: {{ cart.total_price | money_without_currency }},
    items: [
      {% for item in cart.items %}
      {
        item_id: '{{ item.product_id }}',
        item_name: '{{ item.product.title | escape }}',
        price: {{ item.final_price | money_without_currency }},
        quantity: {{ item.quantity }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
});
</script>

Add Shipping Info (Shopify Plus Only)

Track shipping method selection in checkout.liquid:

{% if checkout.step == 'shipping_method' %}
<script>
  gtag('event', 'add_shipping_info', {
    currency: '{{ checkout.currency }}',
    value: {{ checkout.total_price | money_without_currency }},
    coupon: '{% if checkout.discount %}{{ checkout.discount.code }}{% endif %}',
    shipping_tier: '{{ checkout.shipping_method.title }}',
    items: [
      {% for line_item in checkout.line_items %}
      {
        item_id: '{{ line_item.product_id }}',
        item_name: '{{ line_item.title | escape }}',
        quantity: {{ line_item.quantity }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>
{% endif %}

Add Payment Info (Shopify Plus Only)

Track when customer reaches payment step in checkout.liquid:

{% if checkout.step == 'payment_method' %}
<script>
  gtag('event', 'add_payment_info', {
    currency: '{{ checkout.currency }}',
    value: {{ checkout.total_price | money_without_currency }},
    coupon: '{% if checkout.discount %}{{ checkout.discount.code }}{% endif %}',
    payment_type: 'credit_card',
    items: [
      {% for line_item in checkout.line_items %}
      {
        item_id: '{{ line_item.product_id }}',
        item_name: '{{ line_item.title | escape }}',
        quantity: {{ line_item.quantity }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>
{% endif %}

Purchase Event (All Stores)

Track completed purchases on order confirmation page.

Go to: Settings → Checkout → Order status page → Additional scripts

<script>
  gtag('event', 'purchase', {
    transaction_id: '{{ order.order_number }}',
    affiliation: '{{ shop.name }}',
    value: {{ total_price | money_without_currency }},
    currency: '{{ currency }}',
    tax: {{ tax_price | money_without_currency }},
    shipping: {{ shipping_price | money_without_currency }},
    coupon: '{% if discount %}{{ discount.code }}{% endif %}',
    items: [
      {% for line_item in line_items %}
      {
        item_id: '{{ line_item.sku }}',
        item_name: '{{ line_item.title | escape }}',
        item_brand: '{{ line_item.vendor | escape }}',
        item_category: '{{ line_item.product.type | escape }}',
        item_variant: '{{ line_item.variant_title | escape }}',
        price: {{ line_item.final_price | money_without_currency }},
        quantity: {{ line_item.quantity }},
        discount: {{ line_item.total_discount | money_without_currency }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>

Testing Your Ecommerce Tracking

1. Use GA4 DebugView

  • Enable debug mode
  • Walk through full purchase funnel
  • Verify all events fire with correct parameters

2. Verify Items Array Structure

Each item must include:

  • item_id (required)
  • item_name (required)
  • price (recommended)
  • quantity (recommended)

3. Check Value Calculations

  • value should be numeric (no currency symbols)
  • Convert Shopify money format: \{\{ price | money_without_currency \}\}
  • Multiply price by quantity for multi-item carts

4. Test Edge Cases

  • Multiple items in cart
  • Apply discount codes
  • Remove items from cart
  • Different shipping methods (Plus)
  • Guest vs logged-in checkout

Common Issues

Duplicate Purchase Events

Cause: Multiple GA4 implementations or page refreshes.

Fix:

  • Check for duplicate scripts in Additional Scripts
  • Implement transaction deduplication
  • Ensure purchase event fires only once per transaction
// Add transaction check
if (!localStorage.getItem('ga4_purchase_' + transactionId)) {
  gtag('event', 'purchase', {...});
  localStorage.setItem('ga4_purchase_' + transactionId, 'true');
}

Missing Checkout Events

Cause: Non-Plus store or incorrect step detection.

Fix: Only Plus stores can track mid-checkout events. Non-Plus stores can only track begin_checkout (on cart page) and purchase (order confirmation).

Incorrect Currency or Value

Cause: Shopify money format includes currency symbols.

Fix: Always use money_without_currency filter:

{{ price | money_without_currency }}  // Correct: 19.99
{{ price }}  // Wrong: $19.99

Items Array Empty

Cause: Variable not capturing product data correctly.

Fix: Log data layer to console and verify structure matches your variable paths.

Next Steps

For general ecommerce concepts, see GA4 Ecommerce Guide.

// SYS.FOOTER