Webflow Ecommerce + GA4 Tracking | OpsBlu Docs

Webflow Ecommerce + GA4 Tracking

Complete guide to tracking Webflow Ecommerce events with Google Analytics 4, including product views, add to cart, checkout, and purchases.

This guide provides comprehensive implementation instructions for tracking the complete ecommerce journey in Webflow with Google Analytics 4 (GA4), from product impressions to purchase completion.

Prerequisites

  • Webflow Ecommerce plan (Standard, Plus, or Advanced)
  • GA4 installed via custom code method
  • Published Webflow site with ecommerce products configured
  • GA4 Measurement ID (format: G-XXXXXXXXXX)

Webflow Ecommerce Architecture

Understanding Webflow's ecommerce structure is essential for proper tracking:

Page Types

  • Product Pages: Individual product detail pages (CMS Collection Pages)
  • Category Pages: Collection List pages showing multiple products
  • Cart Page: /checkout - Shopping cart review
  • Checkout Page: /checkout - Payment and shipping information
  • Order Confirmation: /order-confirmation - Post-purchase success page

Webflow Ecommerce Objects

Webflow exposes ecommerce data through the global Webflow.commerce object on ecommerce pages. This includes:

  • Cart data: window.Webflow?.commerce?.cart
  • Order data: Available on order confirmation page
  • Product data: Available on product pages

GA4 Ecommerce Events Overview

Implement these GA4 recommended ecommerce events:

  1. view_item_list - Product category/collection pages
  2. view_item - Individual product page views
  3. add_to_cart - Adding products to cart
  4. remove_from_cart - Removing products from cart
  5. view_cart - Viewing the cart page
  6. begin_checkout - Starting the checkout process
  7. add_payment_info - Adding payment information
  8. purchase - Completed purchase

Implementation Strategy

Method 1: Direct Implementation (Basic)

Install tracking code directly in Webflow Custom Code.

Method 2: Google Tag Manager (Advanced)

Use GTM with a Webflow ecommerce data layer. See GTM Data Layer Guide.

This guide focuses on Method 1 (Direct Implementation).

1. Product List Impressions (view_item_list)

Track when users see products on category or collection pages.

Implementation

Add this code to your Collection List Page template (or Project Settings > Custom Code > Footer):

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Find all product items in collection list
    const productItems = document.querySelectorAll('.w-dyn-item');

    if (productItems.length > 0) {
      const items = [];

      productItems.forEach(function(item, index) {
        const productName = item.querySelector('.product-name')?.textContent.trim() || 'Unknown Product';
        const productPrice = item.querySelector('.product-price')?.textContent.replace(/[^0-9.]/g, '') || '0';
        const productLink = item.querySelector('a')?.getAttribute('href') || '';
        const productId = productLink.split('/').pop() || 'unknown';

        items.push({
          item_id: productId,
          item_name: productName,
          price: parseFloat(productPrice),
          index: index,
          item_category: 'Collection Products' // Customize based on your collection
        });
      });

      // Send view_item_list event
      gtag('event', 'view_item_list', {
        item_list_id: 'webflow_collection',
        item_list_name: 'Product Collection',
        items: items
      });
    }
  });
</script>

Customize selectors: Update .product-name and .product-price to match your Webflow class names.

2. Product Detail View (view_item)

Track individual product page views.

Implementation

Add this code to your Product Template Page as an Embed element or in Custom Code:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Get product data from page elements
    const productName = document.querySelector('.product-name')?.textContent.trim() || 'Unknown Product';
    const productPrice = document.querySelector('.product-price')?.textContent.replace(/[^0-9.]/g, '') || '0';
    const productSku = document.querySelector('.product-sku')?.textContent.trim() || '';
    const productCategory = document.querySelector('.product-category')?.textContent.trim() || 'Uncategorized';

    // Get product ID from URL slug
    const productId = window.location.pathname.split('/').pop();

    // Send view_item event
    gtag('event', 'view_item', {
      currency: 'USD', // Change to your currency
      value: parseFloat(productPrice),
      items: [{
        item_id: productId,
        item_name: productName,
        item_category: productCategory,
        price: parseFloat(productPrice),
        quantity: 1
      }]
    });
  });
</script>

Using CMS Fields

For more accurate data, use Webflow's CMS fields in an Embed element on your Product Template:

<script>
  // Product data from CMS (click "Insert field" to add actual CMS values)
  const productData = {
    id: 'PRODUCT_SKU_FIELD',  // Insert SKU field
    name: 'PRODUCT_NAME_FIELD', // Insert Name field
    price: 'PRODUCT_PRICE_FIELD', // Insert Price field
    category: 'PRODUCT_CATEGORY_FIELD' // Insert Category field
  };

  gtag('event', 'view_item', {
    currency: 'USD',
    value: parseFloat(productData.price),
    items: [{
      item_id: productData.id,
      item_name: productData.name,
      item_category: productData.category,
      price: parseFloat(productData.price),
      quantity: 1
    }]
  });
</script>

In Webflow: Use the purple "Insert field" button in the Embed element to insert actual CMS field values.

3. Add to Cart (add_to_cart)

Track when users add products to their cart.

Implementation

Add this code to Project Settings > Custom Code > Footer Code:

<script>
  // Wait for Webflow commerce to load
  window.Webflow = window.Webflow || [];
  window.Webflow.push(function() {
    // Listen for cart updates
    let lastCartCount = 0;

    function checkCartChanges() {
      const cart = window.Webflow?.commerce?.cart;

      if (cart && cart.items) {
        const currentCount = cart.items.reduce((sum, item) => sum + item.count, 0);

        // If count increased, an item was added
        if (currentCount > lastCartCount) {
          // Find the newly added item(s)
          cart.items.forEach(function(item) {
            gtag('event', 'add_to_cart', {
              currency: cart.currency || 'USD',
              value: parseFloat(item.price) * item.count,
              items: [{
                item_id: item.sku || item.productId,
                item_name: item.name,
                price: parseFloat(item.price),
                quantity: item.count
              }]
            });
          });
        }

        lastCartCount = currentCount;
      }
    }

    // Check for cart changes periodically
    setInterval(checkCartChanges, 1000);

    // Also check on add to cart button clicks
    document.addEventListener('click', function(e) {
      if (e.target.matches('.w-commerce-commerceaddtocartbutton') ||
          e.target.closest('.w-commerce-commerceaddtocartbutton')) {
        setTimeout(checkCartChanges, 500);
      }
    });
  });
</script>

Alternative: Button Click Tracking

A simpler approach that tracks the button click (not the actual cart addition):

<script>
  document.addEventListener('click', function(e) {
    const addToCartButton = e.target.closest('.w-commerce-commerceaddtocartbutton');

    if (addToCartButton) {
      // Get product data from the page
      const productName = document.querySelector('.product-name')?.textContent.trim() || 'Unknown';
      const productPrice = document.querySelector('.product-price')?.textContent.replace(/[^0-9.]/g, '') || '0';
      const productId = window.location.pathname.split('/').pop();

      gtag('event', 'add_to_cart', {
        currency: 'USD',
        value: parseFloat(productPrice),
        items: [{
          item_id: productId,
          item_name: productName,
          price: parseFloat(productPrice),
          quantity: 1
        }]
      });
    }
  });
</script>

4. Remove from Cart (remove_from_cart)

Track when users remove items from their cart.

Implementation

<script>
  window.Webflow = window.Webflow || [];
  window.Webflow.push(function() {
    let previousCart = [];

    function trackCartChanges() {
      const cart = window.Webflow?.commerce?.cart;

      if (cart && cart.items) {
        // Check for removed items
        previousCart.forEach(function(prevItem) {
          const currentItem = cart.items.find(item => item.productId === prevItem.productId);

          // Item was removed
          if (!currentItem) {
            gtag('event', 'remove_from_cart', {
              currency: cart.currency || 'USD',
              value: parseFloat(prevItem.price) * prevItem.count,
              items: [{
                item_id: prevItem.sku || prevItem.productId,
                item_name: prevItem.name,
                price: parseFloat(prevItem.price),
                quantity: prevItem.count
              }]
            });
          }
          // Quantity decreased
          else if (currentItem.count < prevItem.count) {
            const removedCount = prevItem.count - currentItem.count;

            gtag('event', 'remove_from_cart', {
              currency: cart.currency || 'USD',
              value: parseFloat(prevItem.price) * removedCount,
              items: [{
                item_id: prevItem.sku || prevItem.productId,
                item_name: prevItem.name,
                price: parseFloat(prevItem.price),
                quantity: removedCount
              }]
            });
          }
        });

        // Update previous cart state
        previousCart = JSON.parse(JSON.stringify(cart.items));
      }
    }

    // Monitor cart changes
    setInterval(trackCartChanges, 1000);
  });
</script>

5. View Cart (view_cart)

Track when users view their cart page.

Implementation

Add this code to the Cart Page (/checkout) as an Embed element or page-level custom code:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Check if we're on the cart page (not checkout)
    if (window.location.pathname === '/checkout' && !window.location.search.includes('payment')) {
      window.Webflow = window.Webflow || [];
      window.Webflow.push(function() {
        const cart = window.Webflow?.commerce?.cart;

        if (cart && cart.items && cart.items.length > 0) {
          const items = cart.items.map(function(item) {
            return {
              item_id: item.sku || item.productId,
              item_name: item.name,
              price: parseFloat(item.price),
              quantity: item.count
            };
          });

          gtag('event', 'view_cart', {
            currency: cart.currency || 'USD',
            value: parseFloat(cart.subtotal),
            items: items
          });
        }
      });
    }
  });
</script>

6. Begin Checkout (begin_checkout)

Track when users start the checkout process.

Implementation

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Detect checkout start (when payment/shipping form is visible)
    const checkoutForm = document.querySelector('.w-commerce-commercecheckoutform');

    if (checkoutForm) {
      window.Webflow = window.Webflow || [];
      window.Webflow.push(function() {
        const cart = window.Webflow?.commerce?.cart;

        if (cart && cart.items && cart.items.length > 0) {
          const items = cart.items.map(function(item) {
            return {
              item_id: item.sku || item.productId,
              item_name: item.name,
              price: parseFloat(item.price),
              quantity: item.count
            };
          });

          gtag('event', 'begin_checkout', {
            currency: cart.currency || 'USD',
            value: parseFloat(cart.subtotal),
            items: items
          });
        }
      });
    }
  });
</script>

7. Purchase (purchase)

Track completed purchases on the order confirmation page.

Implementation

Add this code to Project Settings > Custom Code > Footer Code or as page-level code on the order confirmation page:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Check if we're on the order confirmation page
    if (window.location.pathname.includes('/order-confirmation') ||
        document.querySelector('.w-commerce-commerceorderconfirmationcontainer')) {

      window.Webflow = window.Webflow || [];
      window.Webflow.push(function() {
        // Get order data from Webflow
        const orderData = window.Webflow?.commerce?.order;

        if (orderData) {
          const items = orderData.userItems.map(function(item) {
            return {
              item_id: item.sku || item.productId,
              item_name: item.name,
              price: parseFloat(item.price),
              quantity: item.count
            };
          });

          gtag('event', 'purchase', {
            transaction_id: orderData.orderId,
            value: parseFloat(orderData.total),
            tax: parseFloat(orderData.tax || 0),
            shipping: parseFloat(orderData.shipping || 0),
            currency: orderData.currency || 'USD',
            coupon: orderData.coupon || '',
            items: items
          });
        }
      });
    }
  });
</script>

Prevent Duplicate Purchase Events

Add a flag to prevent duplicate tracking if users refresh the confirmation page:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    if (window.location.pathname.includes('/order-confirmation')) {
      // Check if we've already tracked this order
      const trackingKey = 'webflow_order_tracked';
      const trackedOrders = JSON.parse(localStorage.getItem(trackingKey) || '[]');

      window.Webflow = window.Webflow || [];
      window.Webflow.push(function() {
        const orderData = window.Webflow?.commerce?.order;

        if (orderData && !trackedOrders.includes(orderData.orderId)) {
          const items = orderData.userItems.map(function(item) {
            return {
              item_id: item.sku || item.productId,
              item_name: item.name,
              price: parseFloat(item.price),
              quantity: item.count
            };
          });

          gtag('event', 'purchase', {
            transaction_id: orderData.orderId,
            value: parseFloat(orderData.total),
            tax: parseFloat(orderData.tax || 0),
            shipping: parseFloat(orderData.shipping || 0),
            currency: orderData.currency || 'USD',
            items: items
          });

          // Mark this order as tracked
          trackedOrders.push(orderData.orderId);
          localStorage.setItem(trackingKey, JSON.stringify(trackedOrders.slice(-10))); // Keep last 10
        }
      });
    }
  });
</script>

Complete Implementation Example

Here's a complete, production-ready implementation combining all events:

<!-- Add to Project Settings > Custom Code > Footer Code -->
<script>
  (function() {
    'use strict';

    // Configuration
    const config = {
      currency: 'USD',
      debug: false // Set to true for console logging
    };

    function log(message, data) {
      if (config.debug) {
        console.log('[Webflow GA4]', message, data);
      }
    }

    // Helper: Get product data from page
    function getProductDataFromPage() {
      return {
        id: window.location.pathname.split('/').pop(),
        name: document.querySelector('.product-name')?.textContent.trim() || 'Unknown',
        price: document.querySelector('.product-price')?.textContent.replace(/[^0-9.]/g, '') || '0',
        category: document.querySelector('.product-category')?.textContent.trim() || 'Uncategorized'
      };
    }

    // Helper: Format cart items for GA4
    function formatCartItems(items) {
      return items.map(function(item) {
        return {
          item_id: item.sku || item.productId,
          item_name: item.name,
          price: parseFloat(item.price),
          quantity: item.count
        };
      });
    }

    // Track product page view
    function trackProductView() {
      if (document.querySelector('.w-commerce-commerceproducttemplate')) {
        const product = getProductDataFromPage();

        gtag('event', 'view_item', {
          currency: config.currency,
          value: parseFloat(product.price),
          items: [{
            item_id: product.id,
            item_name: product.name,
            item_category: product.category,
            price: parseFloat(product.price),
            quantity: 1
          }]
        });

        log('Product view tracked', product);
      }
    }

    // Track add to cart
    function trackAddToCart() {
      document.addEventListener('click', function(e) {
        const button = e.target.closest('.w-commerce-commerceaddtocartbutton');

        if (button && !button.disabled) {
          const product = getProductDataFromPage();

          gtag('event', 'add_to_cart', {
            currency: config.currency,
            value: parseFloat(product.price),
            items: [{
              item_id: product.id,
              item_name: product.name,
              price: parseFloat(product.price),
              quantity: 1
            }]
          });

          log('Add to cart tracked', product);
        }
      });
    }

    // Track cart view and checkout
    function trackCartAndCheckout() {
      if (!window.location.pathname.includes('/checkout')) return;

      window.Webflow = window.Webflow || [];
      window.Webflow.push(function() {
        const cart = window.Webflow?.commerce?.cart;
        if (!cart || !cart.items || cart.items.length === 0) return;

        const items = formatCartItems(cart.items);
        const value = parseFloat(cart.subtotal);

        // View cart
        if (!window.location.search) {
          gtag('event', 'view_cart', {
            currency: cart.currency || config.currency,
            value: value,
            items: items
          });

          log('View cart tracked', { value, items });
        }

        // Begin checkout
        if (document.querySelector('.w-commerce-commercecheckoutform')) {
          gtag('event', 'begin_checkout', {
            currency: cart.currency || config.currency,
            value: value,
            items: items
          });

          log('Begin checkout tracked', { value, items });
        }
      });
    }

    // Track purchase
    function trackPurchase() {
      if (!window.location.pathname.includes('/order-confirmation')) return;

      const trackingKey = 'webflow_tracked_orders';
      const trackedOrders = JSON.parse(localStorage.getItem(trackingKey) || '[]');

      window.Webflow = window.Webflow || [];
      window.Webflow.push(function() {
        const order = window.Webflow?.commerce?.order;
        if (!order || trackedOrders.includes(order.orderId)) return;

        const items = order.userItems.map(function(item) {
          return {
            item_id: item.sku || item.productId,
            item_name: item.name,
            price: parseFloat(item.price),
            quantity: item.count
          };
        });

        gtag('event', 'purchase', {
          transaction_id: order.orderId,
          value: parseFloat(order.total),
          tax: parseFloat(order.tax || 0),
          shipping: parseFloat(order.shipping || 0),
          currency: order.currency || config.currency,
          items: items
        });

        log('Purchase tracked', order);

        // Prevent duplicate tracking
        trackedOrders.push(order.orderId);
        localStorage.setItem(trackingKey, JSON.stringify(trackedOrders.slice(-10)));
      });
    }

    // Initialize tracking
    document.addEventListener('DOMContentLoaded', function() {
      trackProductView();
      trackAddToCart();
      trackCartAndCheckout();
      trackPurchase();

      log('Webflow GA4 Ecommerce tracking initialized');
    });
  })();
</script>

Testing Your Implementation

1. Use GA4 DebugView

Enable debug mode in your GA4 config:

<script>
  gtag('config', 'G-XXXXXXXXXX', {
    'debug_mode': true
  });
</script>

Then check Admin > DebugView in GA4 to see events in real-time.

2. Test Each Event

Create a test checklist:

  • view_item: Visit a product page
  • add_to_cart: Add a product to cart
  • view_cart: View cart page
  • begin_checkout: Start checkout
  • purchase: Complete a test order
  • Verify all events appear in DebugView
  • Verify item data (name, price, quantity) is correct
  • Verify currency and values are correct

3. Browser Console Testing

Check browser console for errors:

// Verify Webflow commerce object
console.log(window.Webflow?.commerce);

// Check cart data
console.log(window.Webflow?.commerce?.cart);

// Check order data (on confirmation page)
console.log(window.Webflow?.commerce?.order);

// Verify gtag is defined
console.log(typeof gtag);

4. Test Purchases in Webflow

Webflow provides test mode for ecommerce:

  1. Enable Test Mode in Ecommerce settings
  2. Use test credit card: 4242 4242 4242 4242
  3. Complete a test purchase
  4. Verify purchase event fires with correct data
  5. Check that transaction_id matches order ID

Common Issues

Purchase Event Not Firing

Problem: Purchase event doesn't fire on order confirmation page.

Solutions:

  1. Verify order data: Check window.Webflow?.commerce?.order in console
  2. Wait for Webflow to load: Wrap in window.Webflow.push(function() {})
  3. Check page detection: Verify URL includes /order-confirmation
  4. Remove duplicate prevention: Temporarily disable localStorage check
  5. Test on published site: Order confirmation only works on published sites

Cart Data Unavailable

Problem: window.Webflow?.commerce?.cart returns undefined.

Solutions:

  1. Ecommerce plan required: Ensure you have a Webflow Ecommerce plan
  2. Wait for Webflow: Use window.Webflow.push()
  3. Products configured: Ensure products exist and are published
  4. Check page type: Cart object only available on ecommerce pages

Incorrect Product Prices

Problem: Product prices are wrong in GA4.

Solutions:

  1. Check selectors: Update .product-price to match your class names
  2. Parse correctly: Use replace(/[^0-9.]/g, '') to remove currency symbols
  3. Use CMS fields: Insert price directly from CMS when possible
  4. Check decimal places: Ensure prices are floats, not strings

Advanced Scenarios

Multiple Currencies

If your store supports multiple currencies:

<script>
  // Detect currency from Webflow
  window.Webflow = window.Webflow || [];
  window.Webflow.push(function() {
    const cart = window.Webflow?.commerce?.cart;
    const currency = cart?.currency || 'USD';

    // Use detected currency in all events
    gtag('event', 'view_item', {
      currency: currency,
      // ... rest of event
    });
  });
</script>

Product Variants

Track product variants (size, color, etc.):

<script>
  const variant = document.querySelector('.w-commerce-commerceproductoption')?.value || 'default';

  gtag('event', 'view_item', {
    items: [{
      item_id: productData.id,
      item_name: productData.name,
      item_variant: variant, // e.g., "Large / Blue"
      price: parseFloat(productData.price),
      quantity: 1
    }]
  });
</script>

Custom Product Categories

Organize products by custom categories:

<script>
  gtag('event', 'view_item', {
    items: [{
      item_id: productData.id,
      item_name: productData.name,
      item_category: 'Clothing',
      item_category2: 'Shirts',
      item_category3: 'T-Shirts',
      item_category4: 'Men',
      price: parseFloat(productData.price),
      quantity: 1
    }]
  });
</script>

Next Steps