Dynamic UTM Parameter Capture
Campaign Attribution & Tracking
Automatically captures UTM parameters from the URL, stores them in localStorage for session persistence, and pushes attribution data to Google Tag Manager's dataLayer for comprehensive campaign tracking.
Key Features
Script Overview
What It Does
This script automatically captures UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) from the URL when a user first visits your site, stores them in localStorage for persistence across sessions, and pushes the data to Google Tag Manager's dataLayer for comprehensive campaign attribution.
Use Case
Perfect for B2B websites that need persistent campaign attribution across sessions. Essential for understanding which marketing campaigns drive the most valuable visitors and conversions, especially for longer sales cycles where users visit multiple times before converting.
Key Benefits
- •Persistent campaign attribution across sessions
- •Better understanding of marketing performance
- •Enhanced analytics data quality
- •Improved conversion tracking accuracy
- •First-touch attribution modeling
- •Cross-session user journey tracking
JavaScript Code
<script>
(function() {
'use strict';
// Dynamic UTM Parameter Capture Script
// Version: 1.2
// Description: Captures UTM parameters and stores them for attribution
// Last Updated: 2025-09-09
// Configuration
var config = {
// UTM Parameters to capture
paramNames: ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'],
// Additional custom parameters (add more as needed)
customParams: ['gclid', 'fbclid', 'msclkid', 'ref', 'referrer'],
// LocalStorage key for storing UTM data
storageKey: 'utm_data',
// DataLayer event name
dataLayerEvent: 'utm_captured',
// Expiration time in days (30 days default)
expirationDays: 30,
// Override existing UTM data (false = keep first-touch)
overrideExisting: false,
// Debug mode (set to true for console logging)
debug: false
};
// Utility functions
function debugLog(message, data) {
if (config.debug) {
console.log('[UTM Capture] ' + message, data || '');
}
}
function getUrlParameters() {
var params = {};
var urlParams = new URLSearchParams(window.location.search);
// Capture UTM parameters
config.paramNames.forEach(function(param) {
var value = urlParams.get(param);
if (value) {
params[param] = value;
}
});
// Capture custom parameters
config.customParams.forEach(function(param) {
var value = urlParams.get(param);
if (value) {
params[param] = value;
}
});
return params;
}
function getStoredUtmData() {
try {
var stored = localStorage.getItem(config.storageKey);
if (stored) {
var data = JSON.parse(stored);
// Check if data has expired
if (data.timestamp && data.expirationDays) {
var expirationTime = data.timestamp + (data.expirationDays * 24 * 60 * 60 * 1000);
if (Date.now() > expirationTime) {
debugLog('UTM data expired, clearing storage');
localStorage.removeItem(config.storageKey);
return null;
}
}
return data;
}
} catch (e) {
debugLog('Error reading stored UTM data:', e);
}
return null;
}
function storeUtmData(params) {
try {
var dataToStore = {
...params,
timestamp: Date.now(),
expirationDays: config.expirationDays,
pageUrl: window.location.href,
referrer: document.referrer || 'direct'
};
localStorage.setItem(config.storageKey, JSON.stringify(dataToStore));
debugLog('UTM data stored:', dataToStore);
return dataToStore;
} catch (e) {
debugLog('Error storing UTM data:', e);
return null;
}
}
function pushToDataLayer(utmData) {
// Ensure dataLayer exists
window.dataLayer = window.dataLayer || [];
// Prepare event data
var eventData = {
event: config.dataLayerEvent,
utm_data: utmData,
utm_source: utmData.utm_source || '',
utm_medium: utmData.utm_medium || '',
utm_campaign: utmData.utm_campaign || '',
utm_term: utmData.utm_term || '',
utm_content: utmData.utm_content || '',
first_touch_timestamp: utmData.timestamp,
attribution_type: utmData.isNewCapture ? 'first_touch' : 'existing'
};
// Add custom parameters to event data
config.customParams.forEach(function(param) {
if (utmData[param]) {
eventData[param] = utmData[param];
}
});
// Push to dataLayer
window.dataLayer.push(eventData);
debugLog('Data pushed to dataLayer:', eventData);
}
// Main execution
function initUTMCapture() {
debugLog('Initializing UTM capture');
// Get current URL parameters
var currentParams = getUrlParameters();
var hasNewParams = Object.keys(currentParams).length > 0;
// Get stored UTM data
var storedData = getStoredUtmData();
debugLog('Current URL params:', currentParams);
debugLog('Stored UTM data:', storedData);
var finalUtmData = null;
if (hasNewParams) {
// New UTM parameters found in URL
if (!storedData || config.overrideExisting) {
// Store new parameters (first-touch or override mode)
finalUtmData = storeUtmData(currentParams);
if (finalUtmData) {
finalUtmData.isNewCapture = true;
debugLog('New UTM parameters captured and stored');
}
} else {
// Keep existing stored data (first-touch attribution)
finalUtmData = storedData;
finalUtmData.isNewCapture = false;
debugLog('Using existing stored UTM data (first-touch)');
}
} else if (storedData) {
// No new parameters, but we have stored data
finalUtmData = storedData;
finalUtmData.isNewCapture = false;
debugLog('Using existing stored UTM data');
}
// Push to dataLayer if we have UTM data
if (finalUtmData) {
pushToDataLayer(finalUtmData);
} else {
debugLog('No UTM parameters to process');
// Still push an event for direct traffic
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: config.dataLayerEvent,
utm_source: 'direct',
utm_medium: 'none',
attribution_type: 'direct_traffic'
});
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initUTMCapture);
} else {
// DOM is already ready
initUTMCapture();
}
// Optional: Expose utility functions for advanced users
window.utmCapture = {
getStoredData: function() {
return getStoredUtmData();
},
clearStoredData: function() {
localStorage.removeItem(config.storageKey);
debugLog('UTM data cleared from storage');
},
refreshCapture: function() {
initUTMCapture();
}
};
})();
</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.