Duplicate Events | Blue Frog Docs

Duplicate Events

Diagnose and fix duplicate event tracking that inflates metrics and compromises data accuracy

Duplicate Events

What This Means

Duplicate events occur when the same user action is tracked multiple times, causing inflated metrics and inaccurate data. This can happen due to multiple tracking codes, improper event listener implementation, or tags firing more than once.

Impact on Your Business

Inflated Metrics:

  • Conversion numbers artificially high
  • Event counts doubled or tripled
  • False sense of performance
  • Cannot trust reported data

Poor Decision Making:

  • Optimizing based on inflated numbers
  • Budget decisions on wrong data
  • A/B test results invalidated
  • ROI calculations incorrect

Wasted Ad Spend:

  • Bidding on inflated conversion data
  • Over-investing in underperforming campaigns
  • Cannot identify true performance
  • Budget allocation errors

How to Diagnose

Method 1: Check Chrome DevTools Network Tab

  1. Open Chrome DevTools (F12)
  2. Navigate to "Network" tab
  3. Filter by "collect" or "analytics"
  4. Trigger event (click button, submit form)
  5. Count how many tracking requests sent

What to Look For:

  • Multiple identical requests
  • Same event sent 2+ times
  • Requests with same parameters
  • Different request URLs for same event

Method 2: Use GA4 DebugView

  1. Enable debug mode
  2. Open GA4 DebugView
  3. Trigger event on your site
  4. Watch event stream

What to Look For:

  • Same event appears multiple times
  • Identical parameters on duplicate events
  • Events firing in rapid succession
  • Same timestamp on multiple events

Method 3: Compare Expected vs Actual Counts

  1. Perform controlled test:

    • Click button exactly 10 times
    • Wait for processing (24 hours)
    • Check analytics report
  2. Calculate ratio:

    Expected: 10 events
    Actual: 20 events
    Ratio: 2x duplication
    

What to Look For:

  • Consistent 2x, 3x, or other multiplier
  • Events always duplicated
  • Pattern in duplication

Method 4: Review Tag Manager Tags

  1. Open Google Tag Manager

  2. Navigate to Tags

  3. Review each tag:

    • Check trigger configurations
    • Look for overlapping triggers
    • Verify tag firing only once
  4. Use Preview mode:

    • Enable preview
    • Trigger event
    • Check "Tags Fired" section
    • Count how many times same tag fires

What to Look For:

  • Same tag appears multiple times in "Tags Fired"
  • Multiple tags tracking same event
  • Overlapping triggers
  • Tags without proper conditions

General Fixes

Fix 1: Remove Duplicate Tracking Codes

Common cause: Multiple installations:

  1. Check for multiple GA4 installations:

    <!-- View page source -->
    <!-- Search for G-XXXXXXXXXX -->
    <!-- Should appear only once -->
    
  2. Check for GTM + hardcoded tags:

    <!-- Bad - both GTM and hardcoded GA4 -->
    <script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
    
    <!-- Good - only GTM (add GA4 inside GTM) -->
    <script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
    
  3. Remove redundant pixels:

    <!-- Check for duplicate Facebook Pixel -->
    <!-- Should appear only once -->
    <script>
      fbq('init', 'YOUR_PIXEL_ID');
      fbq('track', 'PageView');
    </script>
    
  4. Audit all tracking:

    • List all tracking codes/tags
    • Remove duplicates
    • Consolidate in tag manager
    • Test after removal

Fix 2: Fix Event Listener Implementation

Prevent multiple event listener attachments:

  1. Remove existing listeners before adding:

    // Bad - adds new listener every time code runs
    button.addEventListener('click', trackClick);
    
    // Good - remove first, then add
    button.removeEventListener('click', trackClick);
    button.addEventListener('click', trackClick);
    
  2. Use event listener only once:

    // Bad - attaches multiple listeners
    document.querySelectorAll('.buy-button').forEach(button => {
      button.addEventListener('click', trackClick);
    });
    // If code runs twice, listeners doubled
    
    // Good - remove all first, then attach
    function attachListeners() {
      const buttons = document.querySelectorAll('.buy-button');
      buttons.forEach(button => {
        // Clone node to remove all listeners
        const newButton = button.cloneNode(true);
        button.parentNode.replaceChild(newButton, button);
        newButton.addEventListener('click', trackClick);
      });
    }
    
  3. Use event delegation:

    // Good - single listener on parent
    document.body.addEventListener('click', function(e) {
      if (e.target.matches('.buy-button')) {
        trackClick();
      }
    });
    
  4. Add flag to prevent duplicate firing:

    let eventTracked = false;
    
    function trackPurchase() {
      if (eventTracked) return; // Prevent duplicate
    
      gtag('event', 'purchase', {...});
      eventTracked = true;
    }
    

Fix 3: Fix GTM Tag Configuration

Ensure tags fire only once:

  1. Review trigger settings:

    Check trigger conditions:
    - Should fire on specific action
    - Not on all clicks or all page views
    - Use specific selectors
    
  2. Add trigger exceptions:

    Tag: Purchase Tracking
    Triggers: Thank You Page
    Exceptions: None
    
    Add exception:
    - Don't fire if page has already been tracked
    - Use data layer variable to check
    
  3. Use "Once per page" setting:

    Tag Configuration → Advanced Settings
    → Tag firing options
    → Select "Once per page"
    
  4. Check for overlapping tags:

    Tag 1: Purchase event (Trigger: Thank You Page)
    Tag 2: GA4 Purchase (Trigger: Thank You Page)
    Result: Same event tracked twice
    Solution: Remove one tag
    

Fix 4: Implement Debouncing

Prevent rapid-fire duplicate events:

  1. Debounce click events:

    let debounceTimeout;
    
    function trackClick() {
      clearTimeout(debounceTimeout);
      debounceTimeout = setTimeout(() => {
        gtag('event', 'click', {...});
      }, 300); // Wait 300ms before tracking
    }
    
    button.addEventListener('click', trackClick);
    
  2. Throttle events:

    let lastTrack = 0;
    
    function trackScroll() {
      const now = Date.now();
      if (now - lastTrack < 1000) return; // Max once per second
    
      gtag('event', 'scroll', {...});
      lastTrack = now;
    }
    
    window.addEventListener('scroll', trackScroll);
    
  3. Use once option:

    // Event listener fires only once
    button.addEventListener('click', function() {
      gtag('event', 'click', {...});
    }, { once: true });
    

Fix 5: Fix Form Submission Tracking

Prevent double form submission tracking:

  1. Track on form submit, not button click:

    // Bad - tracks on button click (can fire multiple times)
    submitButton.addEventListener('click', () => {
      gtag('event', 'form_submit', {...});
    });
    
    // Good - tracks on form submit (fires once)
    form.addEventListener('submit', function(e) {
      gtag('event', 'form_submit', {...});
    });
    
  2. Prevent default and track before submit:

    form.addEventListener('submit', function(e) {
      e.preventDefault(); // Prevent default submission
    
      // Track event
      gtag('event', 'form_submit', {
        event_callback: () => {
          form.submit(); // Submit after tracking
        }
      });
    });
    
  3. Use hidden field to prevent duplicate:

    form.addEventListener('submit', function(e) {
      const tracked = this.querySelector('input[name="tracked"]');
    
      if (tracked && tracked.value === '1') {
        return; // Already tracked
      }
    
      e.preventDefault();
    
      // Add hidden field
      const input = document.createElement('input');
      input.type = 'hidden';
      input.name = 'tracked';
      input.value = '1';
      this.appendChild(input);
    
      // Track event
      gtag('event', 'form_submit', {...});
    });
    

Fix 6: Fix Single Page Application (SPA) Tracking

Handle client-side navigation:

  1. Track page views properly:

    // Bad - history push tracked multiple times
    router.on('route', () => {
      gtag('event', 'page_view', {...});
    });
    
    // Good - track only on actual navigation
    let lastPath = '';
    
    router.on('route', (path) => {
      if (path !== lastPath) {
        gtag('event', 'page_view', {...});
        lastPath = path;
      }
    });
    
  2. Remove event listeners on unmount:

    // React example
    useEffect(() => {
      const handleClick = () => {
        gtag('event', 'click', {...});
      };
    
      button.addEventListener('click', handleClick);
    
      // Cleanup
      return () => {
        button.removeEventListener('click', handleClick);
      };
    }, []);
    
  3. Use framework-specific tracking:

    // React Router
    import { useEffect } from 'react';
    import { useLocation } from 'react-router-dom';
    
    function Analytics() {
      const location = useLocation();
    
      useEffect(() => {
        gtag('event', 'page_view', {
          page_path: location.pathname
        });
      }, [location]);
    
      return null;
    }
    

Fix 7: Use Server-Side De-duplication

Prevent duplicate server-side events:

  1. Check for existing event:

    # Python example
    import hashlib
    
    def track_event(user_id, event_name, timestamp):
        # Generate unique event ID
        event_id = hashlib.md5(
            f"{user_id}:{event_name}:{timestamp}".encode()
        ).hexdigest()
    
        # Check if already tracked
        if event_exists(event_id):
            return  # Skip duplicate
    
        # Track event
        save_event(event_id, user_id, event_name)
    
  2. Use transaction ID:

    // Client-side
    gtag('event', 'purchase', {
      transaction_id: 'T-' + Date.now(),
      value: 99.99
    });
    
    // Server-side checks transaction_id
    // Prevents same transaction from being counted twice
    

Platform-Specific Guides

Detailed implementation instructions for your specific platform:

Platform Troubleshooting Guide
Shopify Shopify Duplicate Events Guide
WordPress WordPress Duplicate Events Guide
Wix Wix Duplicate Events Guide
Squarespace Squarespace Duplicate Events Guide
Webflow Webflow Duplicate Events Guide

Verification

After implementing fixes:

  1. Test in Chrome DevTools:

    • Open Network tab
    • Trigger event once
    • Verify only one request sent
  2. Test in GTM Preview:

    • Enable preview mode
    • Trigger event
    • Check "Tags Fired" - should show once
  3. Controlled test:

    • Trigger event exactly 10 times
    • Wait 24 hours
    • Verify analytics shows 10 (not 20, 30, etc.)
  4. Monitor ongoing:

    • Compare event counts to expected
    • Check for suspicious spikes
    • Regular data quality audits

Common Mistakes

  1. Multiple tracking codes - GTM + hardcoded
  2. Event listeners attached multiple times - No cleanup
  3. Overlapping GTM tags - Same event tracked twice
  4. Form tracking on both button and form - Fires twice
  5. SPA navigation not handled - Listeners accumulate
  6. No debouncing - Rapid clicks all tracked
  7. Testing without clearing cache - Old code runs
  8. Inline and delegated listeners - Both fire
  9. Not using transaction ID - Can't de-duplicate
  10. Multiple tag managers - GTM + Segment + Tealium

Troubleshooting Checklist

  • Only one tracking code installation
  • No duplicate tags in GTM
  • Event listeners properly cleaned up
  • Debouncing implemented for rapid events
  • Form tracking only on submit (not button click)
  • SPA navigation handled correctly
  • Transaction IDs used for purchases
  • Tested in multiple browsers
  • Verified in GTM Preview mode
  • Checked Network tab for duplicate requests
  • Controlled test shows 1:1 ratio
  • No overlapping triggers in GTM

Additional Resources

// SYS.FOOTER