Webflow Tracking Issues
Platform-specific guides for diagnosing and fixing analytics and tracking issues on Webflow.
Common Issues
Events Not Firing
Debug why analytics events aren't being captured on Webflow.
Webflow-Specific Tracking Challenges
Webflow's visual development platform and publishing system create unique tracking scenarios, especially with its Designer vs published site differences and CMS dynamic content.
Platform Code Injection Points
Webflow provides multiple locations for custom code:
Site-Wide Custom Code (Project Settings → Custom Code):
- Head Code - Loads in
<head>on all pages - Footer Code - Loads before
</body>on all pages - Applies to entire published site
- Separate for staging vs production
Page-Level Custom Code:
- Page Settings → Custom Code → Inside
<head>tag - Page Settings → Custom Code → Before
</body>tag - Overrides or supplements site-wide code
- Useful for page-specific tracking
Embed Elements:
- Custom Code embed element
- HTML Embed element
- Inline within page content
- Limited scope to that element
CMS Item Custom Code:
- Available in CMS collection templates
- Can access dynamic CMS fields
- Useful for product/blog tracking
Code Injection Hierarchy:
Site-wide Head Code
↓
Page-level Head Code
↓
Embed Elements (in DOM order)
↓
Page-level Footer Code
↓
Site-wide Footer Code
Designer vs Published Site
Critical Difference:
- Code in Custom Code sections does not run in the Designer
- Only runs on published site (staging or production)
- Must publish to test tracking implementation
Testing Workflow:
- Add tracking code to Custom Code
- Publish to staging domain
- Test on staging.webflow.io subdomain
- Verify tracking works
- Publish to custom domain
Common Mistake:
// This will NOT work in Webflow Designer
console.log('Testing tracking'); // Won't see this in Designer console
// Must publish and view published site to see output
CMS and Dynamic Content
Webflow CMS adds complexity to tracking:
Accessing CMS Fields in Code: Webflow doesn't expose CMS data to JavaScript directly. Must use embed code in CMS template:
<!-- In CMS Collection Template Page Settings → Before </body> -->
<script>
window.cmsData = {
name: '<span class="w-dyn-bind-empty"></span>',
slug: '<span class="w-dyn-bind-empty"></span>',
category: '<span class="w-dyn-bind-empty"></span>',
price: '<span class="w-dyn-bind-empty"></span>'
};
// Track CMS item view
dataLayer.push({
'event': 'itemView',
'itemName': window.cmsData.name,
'itemCategory': window.cmsData.category
});
</script>
Alternative - Scrape from DOM:
// More reliable - get CMS data from rendered HTML
const itemTitle = document.querySelector('.item-title')?.textContent;
const itemPrice = document.querySelector('.item-price')?.textContent;
const itemCategory = document.querySelector('.category-badge')?.textContent;
dataLayer.push({
'event': 'itemView',
'itemName': itemTitle,
'itemPrice': itemPrice,
'itemCategory': itemCategory
});
E-commerce Tracking (Webflow Ecommerce)
Webflow Ecommerce has limited JavaScript access to cart/order data:
Challenges:
- No native data layer
- Order data not exposed as JavaScript object
- Limited checkout customization
- Must scrape from DOM or use Webflow's Limited API
Add to Cart Tracking:
// Monitor add to cart buttons
document.addEventListener('click', function(e) {
const addToCartBtn = e.target.closest('.w-commerce-commerceaddtocartbutton');
if (addToCartBtn && !addToCartBtn.disabled) {
console.log('Add to cart clicked');
// Get product info from page
const productName = document.querySelector('.w-commerce-commerceitemname')?.textContent;
const productPrice = document.querySelector('.w-commerce-commercepricevalue')?.textContent;
// Small delay to let cart update
setTimeout(function() {
dataLayer.push({
'event': 'addToCart',
'productName': productName?.trim(),
'productPrice': parseFloat(productPrice?.replace(/[^0-9.]/g, ''))
});
}, 100);
}
}, true);
Order Confirmation Tracking:
// On Order Confirmation page template
window.addEventListener('DOMContentLoaded', function() {
// Check if we're on order confirmation
const isOrderConfirmation = window.location.pathname.includes('/order-confirmation');
if (isOrderConfirmation) {
// Scrape order details from page
const orderNumber = document.querySelector('.w-commerce-commerceorderconfirmationorderid')?.textContent;
const orderTotal = document.querySelector('.w-commerce-commerceorderconfirmationtotal')?.textContent;
if (orderNumber && orderTotal) {
const transactionValue = parseFloat(orderTotal.replace(/[^0-9.]/g, ''));
dataLayer.push({
'event': 'purchase',
'transactionId': orderNumber.trim(),
'transactionTotal': transactionValue,
'currency': 'USD' // Adjust based on your store
});
console.log('Purchase tracked:', orderNumber, transactionValue);
}
}
});
Webflow Interactions and Tracking
Webflow's visual interaction system can affect tracking:
IX2 (Interactions 2.0):
- Animations and interactions run before your tracking code may be ready
- Events triggered by interactions need special handling
- Modal opens, tab clicks, etc. need event listeners
Tracking Interaction-Based Events:
// Track tab clicks (Webflow tabs)
document.addEventListener('click', function(e) {
const tabLink = e.target.closest('.w-tab-link');
if (tabLink) {
const tabName = tabLink.textContent.trim();
dataLayer.push({
'event': 'tabClick',
'tabName': tabName
});
}
});
// Track modal/lightbox opens
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.classList && node.classList.contains('w-lightbox')) {
dataLayer.push({
'event': 'lightboxOpen'
});
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
Form Submission Tracking
Webflow forms submit to Webflow's servers, making tracking tricky:
Form Submit Event:
// Track Webflow form submissions
document.addEventListener('submit', function(e) {
const webflowForm = e.target.closest('.w-form');
if (webflowForm) {
const formName = webflowForm.getAttribute('data-name') || webflowForm.getAttribute('name');
dataLayer.push({
'event': 'formSubmit',
'formName': formName
});
console.log('Form submitted:', formName);
}
});
// Also monitor for success/error states
const formObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
const target = mutation.target;
// Check for success message
if (target.classList && target.classList.contains('w-form-done') &&
target.style.display !== 'none') {
dataLayer.push({
'event': 'formSuccess',
'formName': target.closest('.w-form')?.getAttribute('data-name')
});
}
// Check for error message
if (target.classList && target.classList.contains('w-form-fail') &&
target.style.display !== 'none') {
dataLayer.push({
'event': 'formError',
'formName': target.closest('.w-form')?.getAttribute('data-name')
});
}
});
});
// Observe all forms
document.querySelectorAll('.w-form').forEach(form => {
formObserver.observe(form, {
attributes: true,
attributeFilter: ['style'],
subtree: true
});
});
Comprehensive Diagnostic Checklist
1. Verify Code is Published
Critical First Step:
- Code in Custom Code sections only runs on published site
- Check staging site:
yoursite.webflow.io - Then check custom domain if configured
Verify Code is Present:
# View source of published page
# Look for your tracking code in <head> or before </body>
# Search for your GA/GTM ID
2. Check Code Injection Location
Site Settings → Publishing → Custom Code:
- Verify code is in Head Code (for most tracking)
- Check for typos, unclosed tags
- Ensure code is complete
Validation Script:
<!-- Add to Site-wide Head Code -->
<script>
console.log('=== Webflow Tracking Diagnostic ===');
console.log('URL:', window.location.href);
console.log('Path:', window.location.pathname);
console.log('Webflow object:', window.Webflow);
console.log('Published:', !window.location.hostname.includes('.webflow.io'));
console.log('Loaded at:', new Date().toISOString());
</script>
3. Test on Published Site
// Add to Footer Code to verify execution
console.log('Footer code executed');
console.log('dataLayer:', window.dataLayer);
console.log('GTM loaded:', !!window.google_tag_manager);
console.log('GA loaded:', typeof window.gtag);
4. Inspect Webflow Objects
// Check Webflow-specific objects
console.log('Webflow:', window.Webflow);
console.log('jQuery (Webflow includes it):', typeof window.$);
// Check for Webflow Ecommerce
const isEcommerce = document.querySelector('.w-commerce-commercecartcontainerwrapper');
console.log('Ecommerce site:', !!isEcommerce);
// Check for CMS
const isCMS = document.querySelector('[data-wf-page]');
console.log('CMS template:', !!isCMS);
5. Monitor DOM Ready State
// Webflow may use document ready differently
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM Content Loaded');
});
window.addEventListener('load', function() {
console.log('Window loaded');
});
// Webflow's ready event
if (window.Webflow) {
window.Webflow.push(function() {
console.log('Webflow ready');
});
}
Browser DevTools Debugging Steps
Network Tab Analysis
Filter for Tracking Requests:
Filter: /collect|/analytics|/gtm|/pixel|/track
Expected Requests:
google-analytics.com/g/collect - GA4
googletagmanager.com/gtm.js - GTM
facebook.com/tr - Facebook Pixel
webflow.com/api - Webflow's own analytics
Common Issues:
- 404 on tracking script: Wrong URL or blocked
- CORS errors: Cross-domain tracking misconfigured
- Blocked by client: Ad blocker or privacy extension
Console Debugging
// Comprehensive tracking check
console.log('=== Tracking Status ===');
console.log('dataLayer:', window.dataLayer?.length, 'items');
console.log('GTM containers:', Object.keys(window.google_tag_manager || {}));
console.log('GA function:', typeof window.gtag);
console.log('Facebook Pixel:', typeof window.fbq);
console.log('jQuery:', typeof window.$);
console.log('Webflow ready:', typeof window.Webflow?.ready);
// List all global tracking objects
const trackingObjects = ['dataLayer', 'gtag', 'ga', 'fbq', 'analytics', '_kmq'];
trackingObjects.forEach(obj => {
console.log(`${obj}:`, typeof window[obj]);
});
// Check for errors
window.addEventListener('error', function(e) {
console.error('JavaScript error:', e.message, e.filename, e.lineno);
});
Application Tab Storage
// Check cookies
const cookies = document.cookie.split(';').map(c => c.trim());
const trackingCookies = cookies.filter(c =>
c.startsWith('_ga') || c.startsWith('_gid') || c.startsWith('_fb')
);
console.log('Tracking cookies:', trackingCookies);
// Check localStorage
console.log('localStorage keys:', Object.keys(localStorage));
// Check sessionStorage
console.log('sessionStorage keys:', Object.keys(sessionStorage));
Common Symptoms and Causes
| Symptom | Likely Cause | Solution |
|---|---|---|
| Code not running at all | Not published / testing in Designer | Publish and test on live site |
| Tracking works on staging not production | Different custom code for domains | Check custom domain settings |
| Events fire multiple times | Code in multiple locations | Audit all Custom Code locations |
| CMS data not available | No JavaScript access to CMS | Scrape from DOM or use embed in template |
| Form tracking missing | Form submits before tracking fires | Use submit event with small delay |
| Cart events not captured | No Webflow ecommerce API | Use click handlers and DOM scraping |
| Code works then breaks | Webflow updated/broke something | Check Webflow changelog, update code |
| jQuery conflicts | Webflow includes jQuery | Use noConflict or Webflow's jQuery |
| Interactions interfere with tracking | IX2 timing issues | Use delayed tracking or interaction events |
| Preview mode tracking fires | Code runs on all published versions | Add conditional logic to exclude staging |
Tag Manager Troubleshooting
Google Tag Manager Setup
Site Settings → Publishing → Custom Code → Head Code:
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
<!-- End Google Tag Manager -->
Site Settings → Publishing → Custom Code → Footer Code:
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
Data Layer Implementation:
<script>
// Initialize dataLayer before GTM
window.dataLayer = window.dataLayer || [];
// Push page data
dataLayer.push({
'pageType': document.body.className.match(/[\w-]+page/)?.[0] || 'unknown',
'pagePath': window.location.pathname
});
// Webflow ready hook
if (window.Webflow) {
Webflow.push(function() {
dataLayer.push({
'event': 'webflowReady'
});
});
}
</script>
GTM Preview Mode:
- Works on published sites
- May need to clear cache
- Test on staging first
E-commerce Tracking
Product Page Tracking
// Enhanced product view tracking
window.addEventListener('DOMContentLoaded', function() {
const isProductPage = document.querySelector('.w-commerce-commerceproductdetails');
if (isProductPage) {
const productName = document.querySelector('.w-commerce-commerceitemname')?.textContent;
const productPrice = document.querySelector('.w-commerce-commercepricevalue')?.textContent;
const productSKU = document.querySelector('[data-commerce-sku-id]')?.getAttribute('data-commerce-sku-id');
dataLayer.push({
'event': 'productView',
'ecommerce': {
'detail': {
'products': [{
'name': productName?.trim(),
'id': productSKU,
'price': parseFloat(productPrice?.replace(/[^0-9.]/g, ''))
}]
}
}
});
}
});
Cart Interaction Tracking
// Track cart opens
let cartObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'style') {
const cartWrapper = mutation.target.closest('.w-commerce-commercecartcontainerwrapper');
if (cartWrapper && cartWrapper.style.display !== 'none') {
dataLayer.push({
'event': 'cartOpen'
});
}
}
});
});
document.querySelectorAll('.w-commerce-commercecartcontainerwrapper').forEach(cart => {
cartObserver.observe(cart, {
attributes: true,
attributeFilter: ['style']
});
});
// Track checkout button click
document.addEventListener('click', function(e) {
const checkoutBtn = e.target.closest('.w-commerce-commercecartemptystate, .w-commerce-commercecheckoutbutton');
if (checkoutBtn) {
dataLayer.push({
'event': 'beginCheckout'
});
}
});
Cookie and Consent Management
Implementing Cookie Consent
// Simple consent banner implementation
function showConsentBanner() {
const hasConsent = localStorage.getItem('cookieConsent');
if (!hasConsent) {
// Show banner (assumes you have a .cookie-banner element in Webflow)
const banner = document.querySelector('.cookie-banner');
if (banner) {
banner.style.display = 'block';
}
} else if (hasConsent === 'accepted') {
loadTrackingScripts();
}
}
function acceptCookies() {
localStorage.setItem('cookieConsent', 'accepted');
document.querySelector('.cookie-banner').style.display = 'none';
loadTrackingScripts();
}
function loadTrackingScripts() {
// Load Google Analytics
const gaScript = document.createElement('script');
gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
gaScript.async = true;
document.head.appendChild(gaScript);
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
}
// Add click handler to accept button
document.addEventListener('click', function(e) {
if (e.target.matches('.accept-cookies')) {
acceptCookies();
}
});
// Run on page load
showConsentBanner();
Google Consent Mode:
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Set default consent
gtag('consent', 'default', {
'ad_storage': 'denied',
'analytics_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'wait_for_update': 500
});
// Update consent when user accepts
function updateConsent(accepted) {
if (accepted) {
gtag('consent', 'update', {
'ad_storage': 'granted',
'analytics_storage': 'granted',
'ad_user_data': 'granted',
'ad_personalization': 'granted'
});
}
}
</script>
When to Contact Support
Contact Webflow Support When:
Platform-Level Issues:
- Custom Code not saving in project settings
- Published site not reflecting code changes
- Form submissions not working
- Ecommerce checkout bugs
- CMS collection issues
Provide to Webflow:
- Project share link or site URL
- Screenshots of Custom Code settings
- Browser console errors
- Specific pages affected
- Steps to reproduce issue
Contact Analytics Vendor When:
- Events firing correctly but not appearing in reports
- Data discrepancies between platforms
- Tag configuration issues
- API integration problems
Provide to Vendor:
- Network tab showing successful requests
- Console logs with tracking data
- Property/measurement IDs
- Example URLs where tracking fires
Contact Developer/Agency When:
- Complex CMS tracking requirements
- Custom ecommerce tracking beyond standard
- Advanced interaction tracking
- Multi-site tracking coordination
- Custom API integrations
Provide to Developer:
- Webflow project access or site export
- Current tracking implementation code
- Desired tracking specification
- Console errors and network logs
- Test account credentials if needed
General Fixes
For universal tracking concepts, see the Global Tracking Issues Hub.