Home
Smart UTM-Based Content Swapper
🔄

Smart UTM-Based Content Swapper

Dynamic Content Personalization

Intelligent content personalization system that dynamically swaps page elements, headlines, and CTAs based on UTM parameters for campaign-specific messaging and improved conversion rates.

PersonalizationAdvancedPersonalization & CRO

Key Features

Dynamic content replacement
UTM-based personalization
A/B testing support
Campaign-specific messaging
Real-time content swapping
Conversion rate optimization

Script Overview

📖

What It Does

This script enables dynamic content personalization based on UTM parameters, allowing you to show different headlines, CTAs, images, or entire sections based on the traffic source, campaign, or medium. Perfect for creating targeted landing page experiences.

🎯

Use Case

Ideal for campaign-specific landing pages and personalized user experiences. Perfect for showing different content based on traffic source or campaign type.

Key Benefits

  • Campaign-specific content personalization
  • Improved conversion rates through relevance
  • Dynamic A/B testing capabilities
  • Enhanced user experience matching
  • Reduced bounce rates from targeted content
  • Automated content optimization

JavaScript Code

<script>
// Smart UTM-Based Content Swapper Script
// Version: 1.0
// Last Updated: 2025-09-09

(function() {
  'use strict';
  
  var config = {
    // Content mapping based on UTM parameters
    contentMappings: {
      // Example mappings - customize for your needs
      utm_source: {
        'google': {
          headline: 'Found Us on Google? Get 20% Off!',
          cta: 'Claim Google Discount',
          description: 'Special offer for Google visitors'
        },
        'facebook': {
          headline: 'Facebook Exclusive: Limited Time Offer',
          cta: 'Get Facebook Deal',
          description: 'Exclusive offer for Facebook users'
        },
        'linkedin': {
          headline: 'Professional Solution for LinkedIn Users',
          cta: 'Try Professional Plan',
          description: 'Business-focused features for professionals'
        }
      },
      
      utm_medium: {
        'email': {
          headline: 'Welcome Back! Continue Where You Left Off',
          cta: 'Resume Journey',
          description: 'Thanks for clicking from our email'
        },
        'cpc': {
          headline: 'Limited Time: Special Paid Ad Pricing',
          cta: 'Get Ad Pricing',
          description: 'Exclusive pricing for ad visitors'
        },
        'social': {
          headline: 'Social Media Special Offer',
          cta: 'Claim Social Bonus',
          description: 'Special offer for social media visitors'
        }
      },
      
      utm_campaign: {
        'summer2024': {
          headline: 'Summer Sale: 30% Off Everything!',
          cta: 'Shop Summer Sale',
          description: 'Limited time summer promotion'
        },
        'holiday': {
          headline: 'Holiday Special: Gift Bundles Available',
          cta: 'View Holiday Deals',
          description: 'Perfect gifts for the holiday season'
        }
      }
    },
    
    // Element selectors to replace content
    elementSelectors: {
      headline: '[data-utm-headline], .utm-headline, h1.hero-title',
      cta: '[data-utm-cta], .utm-cta, .cta-button',
      description: '[data-utm-description], .utm-description, .hero-description',
      image: '[data-utm-image], .utm-image'
    },
    
    // Fallback content if no UTM match
    fallbackContent: {
      headline: null, // Keep original if no match
      cta: null,
      description: null
    },
    
    // Storage key for UTM data
    storageKey: 'utm_data',
    
    // Animation settings
    animation: {
      enabled: true,
      duration: 300, // milliseconds
      easing: 'ease-in-out'
    },
    
    debug: false
  };

  var swappedElements = [];

  function debugLog(message, data) {
    if (config.debug) {
      console.log('[Content Swapper] ' + message, data || '');
    }
  }

  function getUTMParameters() {
    // First try current URL
    var urlParams = new URLSearchParams(window.location.search);
    var params = {};
    
    ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach(function(param) {
      var value = urlParams.get(param);
      if (value) {
        params[param] = value.toLowerCase();
      }
    });
    
    // If no UTM in URL, try stored data
    if (Object.keys(params).length === 0) {
      try {
        var stored = localStorage.getItem(config.storageKey);
        if (stored) {
          var data = JSON.parse(stored);
          ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach(function(param) {
            if (data[param]) {
              params[param] = data[param].toLowerCase();
            }
          });
        }
      } catch (e) {
        debugLog('Error reading stored UTM data:', e);
      }
    }
    
    return params;
  }

  function findContentMatch(utmParams) {
    var matchedContent = {};
    
    // Check each UTM parameter for content mappings
    Object.keys(utmParams).forEach(function(param) {
      var value = utmParams[param];
      
      if (config.contentMappings[param] && config.contentMappings[param][value]) {
        var paramContent = config.contentMappings[param][value];
        
        // Merge content, with later params taking precedence
        Object.keys(paramContent).forEach(function(contentType) {
          matchedContent[contentType] = paramContent[contentType];
        });
        
        debugLog('Content match found:', { param: param, value: value, content: paramContent });
      }
    });
    
    return matchedContent;
  }

  function animateElementChange(element, newContent, contentType) {
    if (!config.animation.enabled) {
      updateElementContent(element, newContent, contentType);
      return;
    }
    
    // Fade out
    element.style.transition = 'opacity ' + config.animation.duration + 'ms ' + config.animation.easing;
    element.style.opacity = '0';
    
    setTimeout(function() {
      updateElementContent(element, newContent, contentType);
      
      // Fade in
      element.style.opacity = '1';
      
      // Clean up transition
      setTimeout(function() {
        element.style.transition = '';
      }, config.animation.duration);
      
    }, config.animation.duration);
  }

  function updateElementContent(element, newContent, contentType) {
    var originalContent = element.getAttribute('data-original-' + contentType) || 
                         (contentType === 'image' ? element.src : element.textContent);
    
    // Store original content if not already stored
    if (!element.getAttribute('data-original-' + contentType)) {
      element.setAttribute('data-original-' + contentType, originalContent);
    }
    
    // Update content based on type
    switch (contentType) {
      case 'image':
        if (element.tagName === 'IMG') {
          element.src = newContent;
          element.alt = 'Personalized content based on traffic source';
        }
        break;
      
      case 'headline':
      case 'cta':
      case 'description':
        element.textContent = newContent;
        break;
      
      default:
        element.innerHTML = newContent;
    }
    
    // Mark as swapped
    element.setAttribute('data-utm-swapped', 'true');
    element.setAttribute('data-utm-type', contentType);
    
    swappedElements.push({
      element: element,
      contentType: contentType,
      originalContent: originalContent,
      newContent: newContent
    });
    
    debugLog('Content updated:', {
      contentType: contentType,
      element: element,
      original: originalContent,
      new: newContent
    });
  }

  function swapContent(matchedContent) {
    Object.keys(matchedContent).forEach(function(contentType) {
      var newContent = matchedContent[contentType];
      var selector = config.elementSelectors[contentType];
      
      if (!selector || !newContent) return;
      
      var elements = document.querySelectorAll(selector);
      
      Array.prototype.forEach.call(elements, function(element) {
        // Skip if already swapped
        if (element.getAttribute('data-utm-swapped') === 'true') return;
        
        if (config.animation.enabled) {
          animateElementChange(element, newContent, contentType);
        } else {
          updateElementContent(element, newContent, contentType);
        }
      });
    });
  }

  function trackContentSwap(utmParams, matchedContent) {
    window.dataLayer = window.dataLayer || [];
    
    window.dataLayer.push({
      event: 'utm_content_swapped',
      utm_parameters: utmParams,
      swapped_content: matchedContent,
      swapped_elements_count: swappedElements.length,
      page_url: window.location.href
    });
    
    debugLog('Content swap tracked:', {
      utm: utmParams,
      content: matchedContent,
      elements: swappedElements.length
    });
  }

  function revertContent() {
    swappedElements.forEach(function(item) {
      var element = item.element;
      var contentType = item.contentType;
      var originalContent = item.originalContent;
      
      // Revert content
      switch (contentType) {
        case 'image':
          if (element.tagName === 'IMG') {
            element.src = originalContent;
          }
          break;
        
        default:
          element.textContent = originalContent;
      }
      
      // Remove attributes
      element.removeAttribute('data-utm-swapped');
      element.removeAttribute('data-utm-type');
      element.removeAttribute('data-original-' + contentType);
    });
    
    swappedElements = [];
    debugLog('Content reverted to original');
  }

  function initContentSwapping() {
    debugLog('Initializing UTM Content Swapper');
    
    var utmParams = getUTMParameters();
    
    if (Object.keys(utmParams).length === 0) {
      debugLog('No UTM parameters found, keeping original content');
      return;
    }
    
    debugLog('UTM parameters found:', utmParams);
    
    var matchedContent = findContentMatch(utmParams);
    
    if (Object.keys(matchedContent).length === 0) {
      debugLog('No content matches found for UTM parameters');
      return;
    }
    
    debugLog('Content matches found:', matchedContent);
    
    // Wait for DOM to be fully loaded
    setTimeout(function() {
      swapContent(matchedContent);
      trackContentSwap(utmParams, matchedContent);
    }, 100);
  }

  // Initialize when DOM is ready
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initContentSwapping);
  } else {
    initContentSwapping();
  }

  // Expose utility functions
  window.utmContentSwapper = {
    getCurrentUTM: function() {
      return getUTMParameters();
    },
    getSwappedElements: function() {
      return swappedElements;
    },
    revertContent: function() {
      revertContent();
    },
    refreshSwapping: function() {
      revertContent();
      initContentSwapping();
    },
    addContentMapping: function(param, value, content) {
      if (!config.contentMappings[param]) {
        config.contentMappings[param] = {};
      }
      config.contentMappings[param][value] = content;
      debugLog('Content mapping added:', { param: param, value: value, content: content });
    }
  };

})();
</script>

💡 Pro Tip: Copy the entire code block above and paste it directly into a GTM Custom HTML tag. The script is self-contained and ready to use immediately.