Webflow GTM Integration
Complete guide to setting up Google Tag Manager (GTM) on your Webflow site for centralized tracking and tag management.
Overview
Google Tag Manager integrates seamlessly with Webflow, providing a powerful solution for managing all your marketing and analytics tags. Webflow's custom code areas make GTM implementation straightforward while maintaining design flexibility.
Why Use GTM with Webflow?
GTM provides powerful tag management benefits:
- Centralized Management: Control all tracking from one interface
- No Code Deploys: Add/modify tags without republishing
- Version Control: Track changes and roll back if needed
- Preview Mode: Test tags before publishing
- Advanced Triggers: Fire tags based on complex conditions
- Ecommerce Support: Full tracking for Webflow Ecommerce
- CMS Integration: Track dynamic CMS content
Prerequisites
Before implementing GTM on Webflow:
- GTM Account: Create a free account at tagmanager.google.com
- Container: Create a Web container for your site
- Webflow Site Plan: Site plan required (not just Account plan)
- Custom Code Access: Available on all site plans
Installation Methods
Method 1: Site-wide Installation (Recommended)
Install GTM across your entire Webflow site.
Step 1: Get GTM Container Code
- Log in to Google Tag Manager
- Click on your container ID (e.g., GTM-XXXXXX)
- Copy both code snippets provided
Step 2: Add to Webflow Project Settings
- In Webflow Designer, open Project Settings (gear icon)
- Go to the Custom Code tab
- Paste the first GTM snippet in the Head Code section:
<!-- 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 -->
- Paste the second GTM snippet in the Footer Code section:
<!-- 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) -->
- Replace
GTM-XXXXXXwith your actual container ID - Click Save Changes
- Publish your site
Method 2: Page-level Implementation
For specific pages only:
- Select the page in the Pages panel
- Click the gear icon for Page Settings
- Go to Custom Code tab
- Add GTM snippets to page-level Head/Footer Code
- Note: Less efficient for multi-page tracking
Method 3: Embed Component
Alternative using Webflow's HTML Embed:
- Add an HTML Embed component to your page
- Paste GTM noscript code
- Place at top of page body
- Combine with Head Code for full implementation
Data Layer Implementation
Basic Data Layer Setup
Add before the GTM container code in Head Code:
<script>
window.dataLayer = window.dataLayer || [];
// Initialize with page data
dataLayer.push({
'pageType': 'standard',
'pageTitle': document.title,
'pageUrl': window.location.href,
'pagePath': window.location.pathname
});
</script>
CMS Collection Page Data Layer
For dynamic CMS content pages:
<script>
dataLayer.push({
'pageType': 'cms-item',
'collectionName': 'blog', // or your collection name
'itemName': document.querySelector('h1').textContent,
'publishDate': document.querySelector('[data-publish-date]')?.textContent
});
</script>
Ecommerce Product Page Data Layer
For Webflow Ecommerce product pages, add to page-level Head Code:
<script>
// Wait for Webflow commerce data
Webflow.push(function() {
var product = window.Webflow?.commerce?.productData;
if (product) {
dataLayer.push({
'event': 'productView',
'ecommerce': {
'detail': {
'products': [{
'id': product.id,
'name': product.name,
'price': product.price / 100, // Convert cents to dollars
'category': product.category || '',
'variant': product.defaultSku
}]
}
}
});
}
});
</script>
Add to Cart Event
Track when products are added to cart:
<script>
document.addEventListener('DOMContentLoaded', function() {
// Listen for Webflow commerce events
document.querySelectorAll('[data-commerce-add-to-cart-button]').forEach(function(button) {
button.addEventListener('click', function(e) {
var form = e.target.closest('form');
var productName = form.querySelector('[data-wf-product-name]')?.textContent;
var productPrice = form.querySelector('[data-wf-product-price]')?.textContent;
dataLayer.push({
'event': 'addToCart',
'ecommerce': {
'add': {
'products': [{
'name': productName,
'price': parseFloat(productPrice.replace(/[^0-9.]/g, '')),
'quantity': parseInt(form.querySelector('[data-wf-quantity]')?.value || 1)
}]
}
}
});
});
});
});
</script>
Ecommerce Purchase Tracking
Add to Order Confirmation page in Page Settings > Custom Code:
<script>
Webflow.push(function() {
var order = window.Webflow?.commerce?.orderData;
if (order && !sessionStorage.getItem('orderTracked_' + order.orderId)) {
dataLayer.push({
'event': 'purchase',
'ecommerce': {
'purchase': {
'actionField': {
'id': order.orderId,
'revenue': order.total / 100,
'tax': order.tax / 100,
'shipping': order.shippingCost / 100
},
'products': order.lineItems.map(function(item) {
return {
'id': item.productId,
'name': item.name,
'price': item.price / 100,
'quantity': item.quantity
};
})
}
}
});
// Prevent duplicate tracking on page refresh
sessionStorage.setItem('orderTracked_' + order.orderId, 'true');
}
});
</script>
Common GTM Tags for Webflow
Google Analytics 4 Configuration
- In GTM, create GA4 Configuration tag
- Enter Measurement ID (G-XXXXXXXXXX)
- Trigger: All Pages
- Configure ecommerce events separately
GA4 Ecommerce Events
Create separate Event tags for each:
| Event Name | Trigger | Purpose |
|---|---|---|
| view_item | Custom Event: productView | Product page views |
| add_to_cart | Custom Event: addToCart | Cart additions |
| begin_checkout | Page View: /checkout | Checkout starts |
| purchase | Custom Event: purchase | Completed orders |
Meta Pixel Base Code
- Create Custom HTML tag
- Paste Meta Pixel code
- Trigger: All Pages
- Add conversion events as separate tags
LinkedIn Insight Tag
- Create Custom HTML tag
- Add LinkedIn Partner ID
- Trigger: All Pages
- Track conversions on form submissions
Common Triggers
Page View Triggers
| Trigger Name | Type | Conditions |
|---|---|---|
| All Pages | Page View | All pages |
| Homepage | Page View | Page Path equals / |
| Product Pages | Page View | Page URL contains /products/ |
| Blog Posts | Page View | Page URL contains /blog/ |
| Cart Page | Page View | Page Path equals /cart |
| Checkout Pages | Page View | Page URL contains /checkout |
| Thank You Page | Page View | Page URL contains /order-confirmation |
Event Triggers
| Trigger Name | Type | Conditions |
|---|---|---|
| Add to Cart | Custom Event | Event equals addToCart |
| Product View | Custom Event | Event equals productView |
| Purchase | Custom Event | Event equals purchase |
| Form Submit | Form Submission | All forms or specific |
Click Triggers
Create for specific interactions:
// CTA button clicks
Trigger: Click - All Elements
Conditions: Click Classes contains 'cta-button'
// Outbound link clicks
Trigger: Click - Just Links
Conditions: Click URL does NOT contain hostname
Built-in Variables
Enable these variables in GTM:
Page Variables:
- Page URL
- Page Hostname
- Page Path
- Referrer
Click Variables:
- Click Element
- Click Classes
- Click ID
- Click URL
- Click Text
Form Variables:
- Form Element
- Form Classes
- Form ID
- Form Target
Utility Variables:
- Random Number
- Container ID
- Container Version
Custom Variables
Page Type Variable
Type: JavaScript Variable
function() {
var path = window.location.pathname;
if (path === '/') return 'homepage';
if (path.startsWith('/products/')) return 'product';
if (path.startsWith('/blog/')) return 'blog-post';
if (path === '/cart') return 'cart';
if (path.includes('/checkout')) return 'checkout';
if (path.includes('/order-confirmation')) return 'confirmation';
return 'other';
}
Product Price Variable
Type: Data Layer Variable
Data Layer Variable Name: ecommerce.detail.products.0.price
Order Revenue Variable
Type: Data Layer Variable
Data Layer Variable Name: ecommerce.purchase.actionField.revenue
CMS Collection Name Variable
Type: JavaScript Variable
function() {
// Extract from URL or data attributes
var collection = document.querySelector('[data-wf-collection]');
return collection ? collection.getAttribute('data-wf-collection') : '';
}
Form Tracking
Built-in Form Tracking
- Enable Form variables in GTM
- Create Form Submission trigger
- Add conditions for specific forms
- Create GA4 Event tag with event name
form_submit
Custom Form Tracking
For Webflow forms with specific tracking:
<script>
document.addEventListener('DOMContentLoaded', function() {
var forms = document.querySelectorAll('form[data-name]');
forms.forEach(function(form) {
form.addEventListener('submit', function(e) {
dataLayer.push({
'event': 'formSubmit',
'formName': form.getAttribute('data-name'),
'formLocation': window.location.pathname
});
});
});
});
</script>
Newsletter Signup Tracking
<script>
document.querySelectorAll('form[data-name="Newsletter"]').forEach(function(form) {
form.addEventListener('submit', function() {
dataLayer.push({
'event': 'newsletter_signup',
'location': 'footer' // or wherever it appears
});
});
});
</script>
Webflow Interactions Tracking
Track Webflow IX2 interactions:
<script>
// Track specific interactions
document.querySelectorAll('[data-w-id]').forEach(function(element) {
element.addEventListener('click', function() {
dataLayer.push({
'event': 'interaction_click',
'elementId': element.getAttribute('data-w-id'),
'elementText': element.textContent.trim()
});
});
});
</script>
Preview and Debug Mode
Using GTM Preview
- In GTM, click Preview button
- Enter your Webflow site URL (use published URL)
- Click Connect
- Tag Assistant opens in new window
- Navigate through your site
Debugging Tips
View data layer in browser console:
// Show all data layer pushes
console.log(window.dataLayer);
// Monitor new events
var originalPush = dataLayer.push;
dataLayer.push = function() {
console.log('GTM Event:', arguments);
originalPush.apply(dataLayer, arguments);
};
Common Issues to Check
- GTM loads on all pages after publishing
- Data layer initializes before GTM container
- Ecommerce events fire with correct data
- Form submissions tracked properly
- No JavaScript console errors
- Tags don't fire multiple times
- Revenue values formatted correctly
Publishing Workflow
Pre-publish Checklist
- Test all tags in Preview mode
- Verify data in GA4 DebugView
- Test form submissions
- Complete test purchase (if ecommerce)
- Check mobile responsiveness
- Verify page load performance
- Test across browsers
Publishing Steps
- In GTM, click Submit
- Add descriptive version name
- Document changes in description
- Click Publish
- Verify live on published Webflow site
- Monitor for 24-48 hours
Webflow Publishing Requirements
Remember to:
- Publish your Webflow site after adding GTM code
- GTM only works on published site, not in Designer preview
- Re-publish if you update custom code
- Use staging domain for testing before production
Ecommerce Tracking Setup
Required Ecommerce Events
| Event | Trigger | Implementation |
|---|---|---|
| Product Impressions | Collection pages | Auto or custom code |
| Product Views | Product page load | Data layer push |
| Add to Cart | Button click | Event listener |
| Remove from Cart | Cart page | Event listener |
| Checkout | Checkout page load | Page view |
| Purchase | Confirmation page | Data layer on load |
Revenue Attribution
Ensure accurate tracking:
- Convert Webflow prices from cents to dollars (divide by 100)
- Remove currency symbols and formatting
- Include tax and shipping separately
- Track discounts if applicable
- Prevent duplicate purchase events
Troubleshooting
| Issue | Possible Cause | Solution |
|---|---|---|
| GTM not loading | Code not published | Publish Webflow site |
| Tags not firing | Wrong trigger setup | Check trigger conditions |
| Duplicate events | Multiple implementations | Remove redundant code |
| Ecommerce data missing | Commerce API not loaded | Wait for Webflow.push callback |
| Forms not tracked | Wrong form selector | Update form trigger |
| Purchase tracking twice | No deduplication | Add sessionStorage check |
| Wrong revenue values | Cents not converted | Divide by 100 |
| CMS data missing | Wrong selector | Check CMS element attributes |
| Preview mode not working | Using Designer URL | Use published site URL |
| Interactions not tracked | No event listener | Add custom tracking code |
Advanced Configuration
Cross-Domain Tracking
If tracking across multiple domains:
// In GA4 Configuration tag
{
'linker': {
'domains': ['yourdomain.com', 'app.yourdomain.com'],
'accept_incoming': true
}
}
User ID Tracking
For member areas or logged-in users:
<script>
// After user authentication
if (window.Webflow?.memberships?.currentMember) {
dataLayer.push({
'userId': window.Webflow.memberships.currentMember.id,
'userEmail': window.Webflow.memberships.currentMember.email
});
}
</script>
Scroll Depth Tracking
Track how far users scroll:
function() {
var scrollDepth = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
[25, 50, 75, 90].forEach(function(threshold) {
if (scrollDepth >= threshold && !window['scrollTracked_' + threshold]) {
dataLayer.push({
'event': 'scroll_depth',
'scrollPercentage': threshold
});
window['scrollTracked_' + threshold] = true;
}
});
}
Video Tracking
For embedded videos (Vimeo, YouTube):
<script>
// YouTube video tracking
var players = [];
function onYouTubeIframeAPIReady() {
document.querySelectorAll('iframe[src*="youtube.com"]').forEach(function(iframe, i) {
players[i] = new YT.Player(iframe, {
events: {
'onStateChange': function(event) {
if (event.data === YT.PlayerState.PLAYING) {
dataLayer.push({
'event': 'video_start',
'videoTitle': event.target.getVideoData().title
});
}
}
}
});
});
}
</script>
Performance Optimization
Best Practices
- Limit total tags to under 20
- Use async loading for third-party scripts
- Minimize Custom HTML tags
- Remove unused triggers and variables
- Test page load impact regularly
- Use tag sequencing when needed
Performance Monitoring
// Measure GTM load time
window.addEventListener('load', function() {
var perfData = window.performance.timing;
var pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
dataLayer.push({
'event': 'performance_timing',
'pageLoadTime': pageLoadTime
});
});
Webflow-specific Considerations
Designer vs. Published Site
- GTM only functions on published sites
- Test using your published staging domain
- Designer preview won't load external scripts
- Always publish after custom code changes
CMS Limitations
- Limited access to CMS data in custom code
- Use data attributes for dynamic content
- Test across different CMS items
- Collection lists need special handling
Ecommerce Considerations
- Webflow stores prices in cents (convert for tracking)
- Limited checkout customization
- Order data available via Webflow.commerce API
- Test with real transactions in staging
Privacy and Compliance
GDPR Consent Management
Implement consent before loading GTM:
<script>
// Wait for consent
window.addEventListener('cookieConsent', function(e) {
if (e.detail.analytics) {
// Load GTM container
(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');
}
});
</script>
GTM Consent Mode
Use Google's Consent Mode:
// Default consent state
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'analytics_storage': 'denied',
'ad_storage': 'denied',
'wait_for_update': 500
});
Testing Checklist
- GTM container loads on published site
- Data layer initializes correctly
- All page types tracked properly
- Product views fire on product pages
- Add to cart events work
- Purchase tracking on confirmation page
- Forms submit tracking works
- No JavaScript console errors
- Mobile experience tested
- Cross-browser compatibility verified
- Page performance acceptable
Related Guides
- GTM Setup Guide
- Data Layer Implementation
- Google Analytics Setup
- Meta Pixel Integration
- Webflow Integrations Overview