Technical reference for implementing Google Analytics 4 (GA4) on Webflow, covering site-wide custom code injection, CMS Collection dynamic binding syntax, Webflow Ecommerce event tracking, and the Embed element approach.
How GA4 Works on Webflow
Webflow generates static HTML at publish time (or server-rendered pages for CMS-driven content). There is no native GA4 integration -- all tracking is implemented via custom code injection:
- Site Settings > Custom Code > Head Code: Scripts added here are injected into the
<head>of every published page. This is the primary method for site-wide gtag.js deployment. Code only executes on the published site, not in the Webflow Designer. - Page-specific custom code: Each page has its own custom code fields (Head and Before
</body>) accessible via Page Settings. These override or supplement site-wide code. - Embed elements: Inline
<script>blocks placed directly in the page layout via the Embed component. These execute in DOM order and are useful for page-specific tracking tied to visual elements.
Webflow's CMS uses a dynamic binding syntax ({{wf {...} }}) that resolves at publish/render time. This allows you to inject CMS field values (product name, slug, price, category) directly into gtag event parameters without client-side DOM queries. The binding syntax uses HTML entity encoding for quotes (") and requires the PlainText type for analytics values.
Webflow Ecommerce does NOT provide a window.dataLayer or automatic ecommerce events. All ecommerce tracking (view_item, add_to_cart, purchase) must be constructed manually. Product data on ecommerce pages is available through Webflow's standard CSS classes (.w-commerce-commerceproducttitle, .w-commerce-commerceproductprice, .w-commerce-commerceaddtocartbutton) or CMS bindings on template pages.
Webflow sites are static (non-SPA), so each page navigation triggers a full page load and a fresh gtag('config', ...) call. No special SPA handling is needed.
Plan Requirements
- Custom Code: Available on all paid Webflow plans
- Ecommerce Tracking: Requires Ecommerce plan
- Site Settings Access: Available to site owners and editors
Installation Methods
Method 1: Site-Wide Custom Code (Recommended)
Add GA4 tracking to all pages using Webflow's site settings.
Step 1: Access Site Settings
- Open your Webflow project
- Click the Settings icon (gear) in the left panel
- Navigate to Custom Code tab
Step 2: Add GA4 Tracking Code
- In the Head Code section, paste:
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
'send_page_view': true,
'cookie_flags': 'SameSite=None;Secure'
});
</script>
- Replace
G-XXXXXXXXXXwith your Measurement ID - Click Save Changes
- Publish your site
Step 3: Verify Installation
- Visit your published site
- Open browser DevTools (F12)
- Check for gtag.js script in Network tab
- Verify page view in GA4 Real-time reports
Method 2: Page-Specific Custom Code
For page-specific tracking or testing.
Add Code to Individual Pages
- Select a page in the Pages panel
- Click the Settings icon next to the page name
- Go to Custom Code in page settings
- Add GA4 code in Before
</body>tag or Inside<head>tag - Publish changes
Method 3: Embed Element
For inline tracking code within page content.
- Drag an Embed element onto your page
- Paste GA4 tracking code
- Position as needed
- Publish site
Ecommerce Tracking Configuration
Webflow Ecommerce Integration
Track Webflow Ecommerce events for comprehensive store analytics.
Product Page Tracking
Add custom code to product template:
<script>
// Track product views
gtag('event', 'view_item', {
'currency': 'USD',
'value': parseFloat('{{wf {"path":"default-price","type":"PlainText"\} }}'),
'items': [{
'item_id': '{{wf {"path":"slug","type":"PlainText"\} }}',
'item_name': '{{wf {"path":"name","type":"PlainText"\} }}',
'item_category': '{{wf {"path":"category","type":"PlainText"\} }}',
'price': parseFloat('{{wf {"path":"default-price","type":"PlainText"\} }}')
}]
});
</script>
Add to Cart Tracking
Track add-to-cart button clicks:
<script>
document.addEventListener('DOMContentLoaded', function() {
// Select add to cart buttons
const addToCartButtons = document.querySelectorAll('.w-commerce-commerceaddtocartbutton');
addToCartButtons.forEach(function(button) {
button.addEventListener('click', function() {
// Get product data from page
const productName = document.querySelector('.w-commerce-commerceproducttitle').textContent;
const productPrice = parseFloat(document.querySelector('.w-commerce-commerceproductprice').textContent.replace(/[^0-9.-]+/g,""));
gtag('event', 'add_to_cart', {
'currency': 'USD',
'value': productPrice,
'items': [{
'item_name': productName,
'price': productPrice,
'quantity': 1
}]
});
});
});
});
</script>
Checkout Tracking
Add to checkout page:
<script>
// Track begin checkout
gtag('event', 'begin_checkout', {
'currency': 'USD',
'value': parseFloat(document.querySelector('.w-commerce-commercecheckoutorderinfosummaryprice').textContent.replace(/[^0-9.-]+/g,"")),
'items': getCartItems()
});
function getCartItems() {
const items = [];
document.querySelectorAll('.w-commerce-commercecheckoutorderitem').forEach(function(item) {
items.push({
'item_name': item.querySelector('.w-commerce-commerceorderitemproductname').textContent,
'price': parseFloat(item.querySelector('.w-commerce-commercecheckoutordersummaryprice').textContent.replace(/[^0-9.-]+/g,"")),
'quantity': parseInt(item.querySelector('.w-commerce-commercecheckoutorderitemquantity').textContent)
});
});
return items;
}
</script>
Purchase Tracking
Add to order confirmation page:
<script>
// Track purchase on order confirmation
gtag('event', 'purchase', {
'transaction_id': '{{wf {"path":"order-id","type":"PlainText"\} }}',
'value': parseFloat('{{wf {"path":"order-total","type":"PlainText"\} }}'.replace(/[^0-9.-]+/g,"")),
'currency': 'USD',
'tax': parseFloat('{{wf {"path":"tax-total","type":"PlainText"\} }}'.replace(/[^0-9.-]+/g,"")),
'shipping': parseFloat('{{wf {"path":"shipping-total","type":"PlainText"\} }}'.replace(/[^0-9.-]+/g,""))
});
</script>
Advanced Event Tracking
Form Submission Tracking
Track Webflow form submissions:
<script>
document.addEventListener('DOMContentLoaded', function() {
// Track form submissions
const forms = document.querySelectorAll('form');
forms.forEach(function(form) {
form.addEventListener('submit', function(e) {
const formName = form.getAttribute('name') || 'Form';
gtag('event', 'form_submit', {
'form_name': formName,
'page_path': window.location.pathname
});
});
});
});
</script>
CMS Collection Tracking
Track interactions with CMS content:
<script>
// Track CMS collection item views
gtag('event', 'view_item', {
'content_type': 'blog_post',
'item_id': '{{wf {"path":"slug","type":"PlainText"\} }}',
'item_name': '{{wf {"path":"name","type":"PlainText"\} }}',
'item_category': '{{wf {"path":"category","type":"PlainText"\} }}'
});
</script>
Button Click Tracking
Track CTA button interactions:
<script>
document.querySelectorAll('.cta-button').forEach(function(button) {
button.addEventListener('click', function() {
gtag('event', 'cta_click', {
'button_text': this.textContent.trim(),
'button_url': this.getAttribute('href'),
'page_location': window.location.href
});
});
});
</script>
Scroll Depth Tracking
Monitor content engagement:
<script>
let scrollDepths = {25: false, 50: false, 75: false, 100: false};
window.addEventListener('scroll', function() {
const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
Object.keys(scrollDepths).forEach(function(depth) {
if (scrollPercent >= depth && !scrollDepths[depth]) {
gtag('event', 'scroll', {
'percent_scrolled': depth,
'page_path': window.location.pathname
});
scrollDepths[depth] = true;
}
});
});
</script>
Video Interaction Tracking
Track video plays and completions:
<script>
document.querySelectorAll('video').forEach(function(video) {
let hasTrackedPlay = false;
video.addEventListener('play', function() {
if (!hasTrackedPlay) {
gtag('event', 'video_start', {
'video_title': document.title,
'video_url': this.currentSrc
});
hasTrackedPlay = true;
}
});
video.addEventListener('ended', function() {
gtag('event', 'video_complete', {
'video_title': document.title,
'video_url': this.currentSrc
});
});
});
</script>
Webflow Interactions Tracking
Track Webflow Interactions
Monitor Webflow's native interactions:
<script>
// Track Webflow interaction triggers
Webflow.push(function() {
$('.interaction-element').on('click', function() {
gtag('event', 'interaction_trigger', {
'interaction_name': $(this).data('interaction-name'),
'element_class': this.className
});
});
});
</script>
Modal/Lightbox Tracking
Track modal opens:
<script>
document.querySelectorAll('[data-lightbox]').forEach(function(trigger) {
trigger.addEventListener('click', function() {
gtag('event', 'lightbox_open', {
'lightbox_id': this.getAttribute('data-lightbox'),
'page_location': window.location.pathname
});
});
});
</script>
User Authentication Tracking
Member Login Tracking
For Webflow Memberships:
<script>
// Check if user is logged in
if (window.Webflow && window.Webflow.env('memberships')) {
gtag('event', 'login', {
'method': 'Webflow Memberships'
});
gtag('set', 'user_properties', {
'member_status': 'authenticated'
});
}
</script>
Troubleshooting
Tracking Code Not Loading
Issue: GA4 not collecting data
Solutions:
- Verify Measurement ID is correct (starts with G-)
- Check custom code was saved in Site Settings
- Ensure site is published after adding code
- Test on published site, not Webflow Designer
- Clear browser cache and test in incognito
- Check for JavaScript errors in console
Duplicate Page Views
Issue: Multiple page views per visit
Solutions:
- Check for duplicate GA4 code in site settings and page settings
- Remove GA4 from page-specific custom code if using site-wide
- Verify embed elements don't contain duplicate tracking
- Check for GTM also loading GA4
- Review third-party integrations
Ecommerce Events Not Firing
Issue: Product tracking not working
Solutions:
- Verify you're on a Webflow Ecommerce plan
- Test on published site with actual products
- Check product template custom code placement
- Verify Webflow dynamic binding syntax is correct
- Test events in GA4 DebugView
- Ensure JavaScript has no syntax errors
Custom Code Not Executing
Issue: Tracking code doesn't run
Solutions:
- Verify code is in correct location (Head vs Body)
- Check for script tag syntax errors
- Ensure code runs after DOM loads
- Test with simple console.log first
- Publish site after making changes
- Check browser console for errors
CMS Dynamic Binding Issues
Issue: Dynamic data not populating
Solutions:
- Use correct Webflow binding syntax:
{{wf {...} }} - Test binding on published site, not designer
- Verify field names match CMS collection
- Check that template context is correct
- Use plain text binding type for analytics values
Form Tracking Not Working
Issue: Form submissions not tracked
Solutions:
- Ensure form has unique name attribute
- Verify event listener attaches after DOM load
- Check form submission isn't prevented by other scripts
- Test with GA4 DebugView
- Verify form doesn't redirect immediately (preventing event)
Testing and Verification
Enable Debug Mode
Add to your GA4 configuration:
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
</script>
Test in Webflow Designer
Note: Tracking may not work fully in Designer preview. Always test on published site.
Complete Testing Checklist
- Page Views: Navigate between pages on published site
- Ecommerce: Add products, checkout, complete test purchase
- Forms: Submit test form submissions
- Custom Events: Trigger all custom tracked interactions
- Mobile: Test on mobile devices and breakpoints
Browser Console Validation
// Check if gtag is loaded
console.log(typeof gtag);
// View dataLayer
console.log(window.dataLayer);
// Manually test event
gtag('event', 'test_event', {'test': 'value'});
GA4 DebugView
- Enable debug mode in code
- Open GA4 and go to Admin > DebugView
- Interact with your Webflow site
- Verify all events appear correctly
- Check event parameters
Best Practices
Use Site-Wide Settings
For global tracking, always use Site Settings > Custom Code instead of individual page embeds.
Organize Custom Code
Add comments to identify different tracking sections:
<script>
// === GA4 Base Configuration ===
gtag('config', 'G-XXXXXXXXXX');
// === Ecommerce Tracking ===
// Product view tracking code...
// === Form Tracking ===
// Form submission tracking code...
</script>
Test Before Publishing
Always test tracking on a staging or development site before deploying to production.
Use Webflow's CMS Features
Leverage dynamic binding for automatic data population:
'item_name': '{{wf {"path":"name","type":"PlainText"\} }}'
Event Naming Conventions
Use consistent, descriptive event names:
// Good
gtag('event', 'product_click');
// Bad
gtag('event', 'click');