Webflow-Specific GA4 Event Tracking | Blue Frog Docs

Webflow-Specific GA4 Event Tracking

Comprehensive guide to tracking Webflow forms, CMS interactions, and custom events with Google Analytics 4.

Webflow-Specific GA4 Event Tracking

This guide covers tracking Webflow-specific interactions with Google Analytics 4 (GA4), including forms, CMS content, interactions, and custom events unique to Webflow sites.

Prerequisites

Webflow Forms

Webflow's native form component generates a unique form submission event that you can track.

Basic Form Submission Tracking

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

<script>
  // Track Webflow form submissions
  document.addEventListener('DOMContentLoaded', function() {
    // Find all Webflow forms
    const forms = document.querySelectorAll('form[data-name]');

    forms.forEach(function(form) {
      form.addEventListener('submit', function(e) {
        const formName = form.getAttribute('data-name');

        // Send event to GA4
        gtag('event', 'form_submit', {
          'form_name': formName,
          'form_location': window.location.pathname
        });
      });
    });
  });
</script>

Track Form Submission Success

Webflow shows a success message after form submission. Track successful submissions:

<script>
  // Track successful form submissions
  document.addEventListener('DOMContentLoaded', function() {
    // Create a MutationObserver to watch for success message
    const observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        mutation.addedNodes.forEach(function(node) {
          // Check if success message appeared
          if (node.classList && node.classList.contains('w-form-done')) {
            const form = node.previousElementSibling;
            const formName = form ? form.getAttribute('data-name') : 'unknown';

            gtag('event', 'form_success', {
              'form_name': formName,
              'form_location': window.location.pathname
            });
          }
        });
      });
    });

    // Observe all form containers
    const formBlocks = document.querySelectorAll('.w-form');
    formBlocks.forEach(function(block) {
      observer.observe(block, { childList: true });
    });
  });
</script>

Track Form Errors

Track when form validation errors occur:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        mutation.addedNodes.forEach(function(node) {
          // Check if error message appeared
          if (node.classList && node.classList.contains('w-form-fail')) {
            const form = node.previousElementSibling?.previousElementSibling;
            const formName = form ? form.getAttribute('data-name') : 'unknown';

            gtag('event', 'form_error', {
              'form_name': formName,
              'error_message': node.textContent.trim(),
              'form_location': window.location.pathname
            });
          }
        });
      });
    });

    const formBlocks = document.querySelectorAll('.w-form');
    formBlocks.forEach(function(block) {
      observer.observe(block, { childList: true });
    });
  });
</script>

Capture Form Field Data

Track specific form field values (be careful with PII):

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const forms = document.querySelectorAll('form[data-name]');

    forms.forEach(function(form) {
      form.addEventListener('submit', function(e) {
        const formName = form.getAttribute('data-name');

        // Example: Track which product someone inquired about
        const productField = form.querySelector('[name="Product"]');
        const messageField = form.querySelector('[name="Message"]');

        gtag('event', 'contact_form_submit', {
          'form_name': formName,
          'product_interest': productField ? productField.value : 'not_specified',
          'has_message': messageField ? (messageField.value.length > 0) : false
        });
      });
    });
  });
</script>

Privacy warning: Do not track email addresses, phone numbers, names, or other PII without proper consent and data protection measures.

Webflow CMS Tracking

Track interactions with Webflow CMS Collection items.

Track CMS Collection Item Views

Add custom code to your CMS Collection Page template:

  1. Go to your CMS Collection Page in the Designer
  2. Add an Embed element at the top of the page
  3. Add this code:
<script>
  // Get CMS item data from the page
  const itemName = document.querySelector('h1')?.textContent || 'Unknown';
  const itemSlug = window.location.pathname.split('/').pop();

  // Send CMS item view event
  gtag('event', 'view_item', {
    'item_name': itemName,
    'item_category': 'blog_post', // Adjust based on your collection type
    'item_slug': itemSlug,
    'content_type': 'cms_collection_item'
  });
</script>

Track CMS Item Clicks from Collection List

Track when users click on CMS items in a Collection List:

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

    collectionItems.forEach(function(link) {
      link.addEventListener('click', function() {
        const itemName = this.textContent.trim();
        const itemUrl = this.getAttribute('href');

        gtag('event', 'select_content', {
          'content_type': 'cms_item',
          'item_name': itemName,
          'item_url': itemUrl
        });
      });
    });
  });
</script>

Pass CMS Field Data to GA4

Use Webflow's Embed element to pass CMS field data to analytics. In your CMS Collection Page template:

  1. Add an Embed element
  2. Click Insert field to insert CMS field values
  3. Add tracking code:
<script>
  // CMS field data (inserted via Webflow CMS)
  const cmsData = {
    title: "INSERT_CMS_FIELD_NAME", // Use Webflow's insert field feature
    category: "INSERT_CATEGORY_FIELD",
    author: "INSERT_AUTHOR_FIELD",
    publishDate: "INSERT_DATE_FIELD"
  };

  // Send enriched page view
  gtag('event', 'page_view', {
    'page_title': cmsData.title,
    'content_category': cmsData.category,
    'content_author': cmsData.author,
    'publish_date': cmsData.publishDate,
    'content_type': 'blog_post'
  });
</script>

In Webflow: Click the purple "Insert field" button in the Embed editor to dynamically insert CMS values.

Webflow Interactions Tracking

Track Webflow's built-in interactions and animations.

Track Tab Clicks

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const tabs = document.querySelectorAll('.w-tab-link');

    tabs.forEach(function(tab) {
      tab.addEventListener('click', function() {
        const tabName = this.textContent.trim();
        const tabsContainer = this.closest('[data-name]');
        const containerName = tabsContainer ? tabsContainer.getAttribute('data-name') : 'unknown';

        gtag('event', 'tab_click', {
          'tab_name': tabName,
          'tabs_container': containerName,
          'page_location': window.location.pathname
        });
      });
    });
  });
</script>

Track Accordion/Dropdown Clicks

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Track dropdown toggles
    const dropdowns = document.querySelectorAll('.w-dropdown-toggle');

    dropdowns.forEach(function(dropdown) {
      dropdown.addEventListener('click', function() {
        const dropdownName = this.textContent.trim();

        gtag('event', 'dropdown_toggle', {
          'dropdown_name': dropdownName,
          'page_location': window.location.pathname
        });
      });
    });
  });
</script>

Track Lightbox Opens

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Track lightbox triggers
    const lightboxLinks = document.querySelectorAll('[data-wf-lightbox]');

    lightboxLinks.forEach(function(link) {
      link.addEventListener('click', function() {
        const lightboxType = this.getAttribute('data-wf-lightbox');
        const mediaUrl = this.getAttribute('href');

        gtag('event', 'lightbox_open', {
          'lightbox_type': lightboxType,
          'media_url': mediaUrl,
          'page_location': window.location.pathname
        });
      });
    });
  });
</script>

Track Slider/Carousel Interactions

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Track slider arrow clicks
    const sliderArrows = document.querySelectorAll('.w-slider-arrow-left, .w-slider-arrow-right');

    sliderArrows.forEach(function(arrow) {
      arrow.addEventListener('click', function() {
        const direction = this.classList.contains('w-slider-arrow-left') ? 'previous' : 'next';
        const slider = this.closest('.w-slider');
        const sliderName = slider ? slider.getAttribute('data-name') : 'unknown';

        gtag('event', 'slider_interaction', {
          'slider_name': sliderName,
          'direction': direction,
          'page_location': window.location.pathname
        });
      });
    });
  });
</script>

Track Specific Button Clicks

Add a custom attribute to buttons you want to track:

  1. Select the button in Webflow Designer
  2. Go to Settings (gear icon) > Custom Attributes
  3. Add attribute: data-track-event="button_name_here"

Then add this tracking code:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const trackableButtons = document.querySelectorAll('[data-track-event]');

    trackableButtons.forEach(function(button) {
      button.addEventListener('click', function() {
        const eventName = this.getAttribute('data-track-event');
        const buttonText = this.textContent.trim();

        gtag('event', 'button_click', {
          'button_name': eventName,
          'button_text': buttonText,
          'page_location': window.location.pathname
        });
      });
    });
  });
</script>

Automatically track external link clicks:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const links = document.querySelectorAll('a');

    links.forEach(function(link) {
      link.addEventListener('click', function() {
        const href = this.getAttribute('href');

        // Check if link is external
        if (href && (href.startsWith('http') || href.startsWith('https')) && !href.includes(window.location.hostname)) {
          gtag('event', 'click', {
            'event_category': 'outbound',
            'event_label': href,
            'transport_type': 'beacon'
          });
        }
      });
    });
  });
</script>

Track File Downloads

Track PDF and file downloads:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const fileLinks = document.querySelectorAll('a[href$=".pdf"], a[href$=".zip"], a[href$=".doc"], a[href$=".docx"], a[href$=".xls"], a[href$=".xlsx"]');

    fileLinks.forEach(function(link) {
      link.addEventListener('click', function() {
        const fileUrl = this.getAttribute('href');
        const fileName = fileUrl.split('/').pop();
        const fileExtension = fileName.split('.').pop();

        gtag('event', 'file_download', {
          'file_name': fileName,
          'file_extension': fileExtension,
          'file_url': fileUrl,
          'page_location': window.location.pathname
        });
      });
    });
  });
</script>

Scroll Depth Tracking

Track how far users scroll on your pages:

<script>
  (function() {
    let scrollMarks = [25, 50, 75, 90, 100];
    let trackedMarks = [];

    function checkScrollDepth() {
      const windowHeight = window.innerHeight;
      const documentHeight = document.documentElement.scrollHeight;
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const scrollPercent = Math.round((scrollTop / (documentHeight - windowHeight)) * 100);

      scrollMarks.forEach(function(mark) {
        if (scrollPercent >= mark && !trackedMarks.includes(mark)) {
          trackedMarks.push(mark);

          gtag('event', 'scroll', {
            'percent_scrolled': mark,
            'page_location': window.location.pathname
          });
        }
      });
    }

    // Throttle scroll events
    let scrollTimeout;
    window.addEventListener('scroll', function() {
      clearTimeout(scrollTimeout);
      scrollTimeout = setTimeout(checkScrollDepth, 100);
    });
  })();
</script>

Video Tracking

Track Embedded YouTube Videos

Webflow's video embed component uses YouTube's API. Track video interactions:

<script>
  // Load YouTube IFrame API
  var tag = document.createElement('script');
  tag.src = "https://www.youtube.com/iframe_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

  // Track video events
  function onYouTubeIframeAPIReady() {
    var videos = document.querySelectorAll('iframe[src*="youtube.com"]');

    videos.forEach(function(video) {
      var player = new YT.Player(video, {
        events: {
          'onStateChange': function(event) {
            var videoUrl = event.target.getVideoUrl();
            var videoTitle = event.target.getVideoData().title;

            if (event.data == YT.PlayerState.PLAYING) {
              gtag('event', 'video_start', {
                'video_title': videoTitle,
                'video_url': videoUrl
              });
            } else if (event.data == YT.PlayerState.ENDED) {
              gtag('event', 'video_complete', {
                'video_title': videoTitle,
                'video_url': videoUrl
              });
            }
          }
        }
      });
    });
  }
</script>

Track Native HTML5 Video

For HTML5 video elements:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const videos = document.querySelectorAll('video');

    videos.forEach(function(video) {
      const videoName = video.getAttribute('data-name') || video.src;

      video.addEventListener('play', function() {
        gtag('event', 'video_start', {
          'video_name': videoName,
          'video_provider': 'html5'
        });
      });

      video.addEventListener('ended', function() {
        gtag('event', 'video_complete', {
          'video_name': videoName,
          'video_provider': 'html5'
        });
      });

      video.addEventListener('pause', function() {
        if (video.currentTime < video.duration) {
          gtag('event', 'video_pause', {
            'video_name': videoName,
            'video_provider': 'html5',
            'video_percent': Math.round((video.currentTime / video.duration) * 100)
          });
        }
      });
    });
  });
</script>

Search Tracking

If you've built a custom search on your Webflow site:

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Assuming search form has data-name="Search"
    const searchForm = document.querySelector('form[data-name="Search"]');

    if (searchForm) {
      searchForm.addEventListener('submit', function(e) {
        const searchInput = this.querySelector('input[type="search"], input[name="query"]');
        const searchTerm = searchInput ? searchInput.value : '';

        gtag('event', 'search', {
          'search_term': searchTerm,
          'page_location': window.location.pathname
        });
      });
    }
  });
</script>

Webflow Ecommerce Events

For Webflow Ecommerce tracking, see the dedicated guide: Webflow Ecommerce + GA4 Tracking

Custom Dimensions and Parameters

Setting Up Custom Dimensions in GA4

  1. Go to Admin > Custom Definitions in GA4
  2. Click Create custom dimension
  3. Add dimensions like:
    • page_template - Webflow page template name
    • cms_collection - CMS collection name
    • membership_status - Memberstack status
    • user_role - User role/type

Sending Custom Dimensions

<script>
  // Set custom dimensions for all events
  gtag('set', {
    'page_template': 'blog-post',
    'cms_collection': 'articles',
    'site_section': 'blog'
  });

  // Or send with specific events
  gtag('event', 'page_view', {
    'page_template': 'blog-post',
    'cms_collection': 'articles'
  });
</script>

Event Naming Best Practices

Use GA4's recommended event names when possible:

  • login - User login
  • sign_up - User registration
  • search - Site search
  • select_content - Content selection
  • share - Social sharing
  • view_item - Item/product view
  • generate_lead - Lead form submission

Custom Event Naming Conventions

For custom Webflow events:

  • Use lowercase with underscores: form_submit not formSubmit or Form Submit
  • Be descriptive: contact_form_submit not form1
  • Use consistent prefixes: webflow_form_, cms_, interaction_
  • Keep names under 40 characters

Testing Events

Use GA4 DebugView

  1. Add debug mode to your GA4 config:
<script>
  gtag('config', 'G-XXXXXXXXXX', {
    'debug_mode': true
  });
</script>
  1. Go to Admin > DebugView in GA4
  2. Trigger events on your site
  3. See events appear in real-time with full parameters

Browser Console Testing

Test events directly in the browser console:

// Test a form submission event
gtag('event', 'form_submit', {
  'form_name': 'Contact',
  'form_location': '/contact'
});

// Check if gtag is defined
console.log(typeof gtag); // Should output "function"

// Check dataLayer
console.log(window.dataLayer); // Should show array of events

Google Tag Assistant

  1. Install Tag Assistant
  2. Visit your site
  3. Trigger events
  4. Verify events in Tag Assistant

Common Issues

Events Not Firing

Problem: Custom events don't appear in GA4.

Solutions:

  1. Verify GA4 is loaded: Check typeof gtag in console
  2. Check event syntax: Ensure proper gtag('event', 'name', {}) format
  3. Wait for page load: Wrap code in DOMContentLoaded listener
  4. Test on published site: Events don't fire in Webflow Designer
  5. Enable debug mode: Use DebugView to troubleshoot

Events Fire Multiple Times

Problem: Events trigger multiple times per action.

Solutions:

  1. Remove duplicate listeners: Check for multiple script instances
  2. Use once: true option:
element.addEventListener('click', handler, { once: true });
  1. Add event flags:
let formSubmitted = false;
form.addEventListener('submit', function() {
  if (!formSubmitted) {
    formSubmitted = true;
    gtag('event', 'form_submit', {...});
  }
});

CMS Field Data Not Passing

Problem: CMS field values don't appear in events.

Solutions:

  1. Use Embed element: CMS fields only work in Embed elements
  2. Click "Insert field": Use the purple button in Embed editor
  3. Check field name: Ensure field exists in CMS collection
  4. Verify on published site: CMS data only available after publishing

Next Steps

// SYS.FOOTER