Webflow Ecommerce + GA4 Tracking | Blue Frog 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.

Webflow Ecommerce + GA4 Tracking

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

// SYS.FOOTER