Skip to main content
← BlogANALYTICS10 MIN READ

Webflow Conversion Tracking: Forms, Ecommerce and GTM the Right Way

Webflow forms never hit a thank-you URL: detect the inline success state, push one dataLayer event, and fire every ad platform off it.

Ariful IslamAriful IslamFounder · computer engineer ·
inX
ACCURACY = REPORTED ÷ REALILLUSTRATIVE · 30 DAYS
Your store100%
GA482%
Meta68%
Google Ads91%
95%+ healthy85–95 leaking<85 broken

Of all the site builders I work on, Webflow is the one I complain about least. Full code access, per-page custom code, an order confirmation page you can actually put scripts on. And yet I keep auditing Webflow sites where Google Ads shows zero conversions while the inbox fills up with leads. The platform isn't the problem. The setup is.

Where do I put GTM in Webflow?

Project Settings → Custom Code: the GTM script goes in the Head Code box, the noscript snippet in the Body Code box, then publish. That loads the container on every page — one container, site-wide, no exceptions.

Two things trip people up here. First, custom code only works on a paid Site plan; on the free staging plan your GTM snippet sits in settings doing nothing. Second, Webflow also has per-page custom code, and I keep finding sites where someone pasted GTM on six individual pages instead of project-wide. New pages get added, nobody remembers the ritual, part of the funnel goes invisible. And once GTM is on the site, every tag lives in GTM — mixed hardcoded-plus-GTM setups are where double-counting comes from.

A Webflow form submission shows an inline success block without navigating, so a thank-you-page trigger never fires; a .w-form-done watcher pushes form_success instead.
Don't use GTM's Form Submission trigger here — only .w-form-done proves Webflow accepted the submission.
  1. Visitor submits — Webflow posts the data in the background
  2. No navigation — Inline success block, same URL, no view
  3. /thank-you trigger — Waits forever; Google Ads shows zero
  4. .w-form-done watcher — Style flips to display: block
  5. form_success pushed — One Custom Event trigger fires every tag

Why doesn't my thank-you page trigger work on Webflow forms?

Because native Webflow forms don't go anywhere. On submit, Webflow posts the data in the background, hides the form, and shows the inline "Thank you! Your submission has been received!" block. No navigation, no new URL, no pageview. A Google Ads or GA4 trigger waiting for /thank-you waits forever.

Two honest options. One: set a redirect URL on the form so it does navigate to a real thank-you page, then use a normal pageview trigger. Works, but it changes the UX and designers usually hate it. Two, the one I use: leave the inline success message alone and detect it firing.

Webflow marks success by switching the .w-form-done element to display: block. Watch for that and push to the dataLayer:

document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.w-form').forEach(function (wrapper) {
    var form = wrapper.querySelector('form');
    var done = wrapper.querySelector('.w-form-done');
    if (!form || !done) return;
    var fired = false;
    new MutationObserver(function () {
      if (fired || done.style.display !== 'block') return;
      fired = true;
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: 'form_success',
        form_name: form.getAttribute('data-name') || form.id || 'webflow_form'
      });
    }).observe(done, { attributes: true, attributeFilter: ['style'] });
  });
});

Paste that in Project Settings footer code (or a site-wide embed), and every native form on the site reports success with its name attached. In GTM, one Custom Event trigger on form_success now drives your Google Ads conversion tag, your Meta lead event, your GA4 generate_lead — all of them, off the same push. The fired flag matters: without it, style changes can fire the observer more than once and you'll double-count.

How do I track Webflow Ecommerce purchases?

Webflow Ecommerce gives you a real order confirmation page, and that page accepts custom code with order data bound into it — which makes purchase tracking cleaner here than on most builders. Open the Order Confirmation page in the Designer, drop in an HTML Embed, and use the embed's field insertion to bind the order fields (order ID, order total) directly into your script:

<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  event: 'purchase',
  transaction_id: '{{ Order ID }}',   // insert via the embed's Add Field
  value: {{ Order Total }},           // insert via Add Field
  currency: 'USD'
});
</script>

The double-curly placeholders aren't literal code — they're where you insert Webflow's order fields using the embed panel's field button. Once that push exists, GA4 purchase, Google Ads conversion with dynamic value, and Meta Purchase all read from it. Check how the total renders: a currency symbol in the value means it arrives as a string, and Google Ads records the conversion with no value. And place a test order after every redesign of that page — I've seen an embed get quietly deleted and take purchase tracking down for weeks.

Why do Webflow interactions break my triggers?

Because Webflow interactions animate elements in and out constantly, and naive GTM triggers can't tell a design flourish from a conversion. Element Visibility triggers are the usual victim: a success message inside a tab, slider, or multi-step show/hide form can become "visible" to GTM when the step animates in — before anyone submitted anything. Preloaders holding the page at opacity 0 delay visibility triggers in the other direction.

The fix is the same pattern as above: don't infer success from what's on screen. Push an explicit dataLayer event at the moment the thing actually happens, and trigger on that. Explicit events don't care what the animation layer is doing.

What about HubSpot or Marketo forms embedded on Webflow?

Embedded marketing-automation forms need their own success hook, because depending on the embed version and settings they can render inside an iframe — and GTM on your Webflow page can't see submits inside someone else's iframe. Even rendered inline, they aren't Webflow forms, so .w-form-done logic does nothing.

Use the vendor's JavaScript callbacks instead. HubSpot's embed accepts an onFormSubmitted callback in hbspt.forms.create(); Marketo exposes MktoForms2.whenReady() with an onSuccess handler. Same move in both cases: inside the callback, push form_success (with a form name) to the dataLayer of the parent page. Now your embedded forms feed the exact same GTM trigger as your native ones, and every platform stays consistent.

Native Webflow forms, HubSpot and Marketo embeds and the order confirmation page all push into one dataLayer event that fires GA4, Google Ads and Meta tags.
When each platform has its own trigger logic the numbers disagree; one push per success makes them agree.
LayerWhat sits there
InputsNative form .w-form-done, HubSpot onFormSubmitted, Marketo onSuccess handler, Order confirmation embed
dataLayer + GTMform_success and purchase with real order IDs and values, not URL or visibility triggers
DestinationsGA4 generate_lead / purchase, Google Ads conversion, Meta Lead and Purchase, Server-side GTM container

What does a clean Webflow tracking setup look like?

One dataLayer push per meaningful success, and every platform fires off it. That's the whole architecture:

  1. GTM loaded once, site-wide, from Project Settings.
  2. form_success pushed from the success-state watcher (native forms) and vendor callbacks (embedded forms).
  3. purchase pushed from the order confirmation embed with real order ID and value.
  4. Google Ads, GA4, Meta, and anything else triggered only by those events — never by URLs, clicks, or element visibility.

When Google Ads says 41 leads and Meta says 67 for the same month, it's almost always because each platform had its own trigger logic. Collapse it to one push per success and the numbers finally agree, because they're all counting the same moments.

Do I need first-party server-side tracking on Webflow?

If you're spending real money on ads, yes — because even a perfect browser-side setup on Webflow loses a chunk of your conversions before they leave the visitor's device. Ad blockers strip GTM and the Meta Pixel outright. Safari's tracking prevention (WebKit's ITP) caps script-set cookies at seven days, so a visitor who clicks your ad and submits your Webflow form nine days later looks like a brand-new organic lead. Your form inbox is full; your ad platforms see a fraction of it and optimize on that fraction.

Server-side tracking, in plain terms: instead of each visitor's browser reporting straight to Meta and Google (and getting blocked or expired along the way), events go to a small server you control — a server-side Google Tag Manager container running on your own subdomain, like track.yourdomain.com. Because that endpoint is first-party (it's your domain), ad blockers and browser privacy rules treat it very differently, cookies live longer, and the server forwards each conversion to Meta's Conversions API, Google Ads Enhanced Conversions, and GA4 reliably, with an event_id on each one so browser and server copies deduplicate instead of double-counting.

For a Webflow lead-gen site this changes what the ad platforms learn from: higher match quality on the leads you send means Meta and Google optimize toward the people who actually convert. And the dataLayer pushes you just built are the foundation — the same form_success and purchase events feed the server container. You're one layer away, not starting over.

Frequently asked questions

How do I track Webflow form submissions in Google Ads?

Push a dataLayer event when the form's inline success message appears (watch the .w-form-done element switch to display: block), then fire your Google Ads conversion tag from a Custom Event trigger in GTM. Don't use a thank-you page trigger — native Webflow forms never navigate.

Why doesn't my thank-you page conversion fire on Webflow?

Native Webflow forms submit in the background and show an inline success message without changing the URL, so no pageview happens and URL-based triggers never fire. Either set a redirect URL on the form or detect the inline success state with custom code.

Where do I paste the Google Tag Manager code in Webflow?

Project Settings → Custom Code: the main GTM script in Head Code, the noscript fallback in Body Code, then publish. That covers every page — avoid pasting it per-page.

Can I use GTM on Webflow's free plan?

No. Custom code, including GTM, requires a paid Site plan. On the free staging plan the code boxes are there but nothing you paste will run on the published site.

Does Webflow Ecommerce support purchase tracking with order value?

Yes. Add an HTML Embed to the Order Confirmation page and bind the order ID and order total fields into a dataLayer push. GTM then sends the purchase with its real value to GA4, Google Ads, and Meta.

How do I track HubSpot forms embedded on a Webflow site?

Use HubSpot's onFormSubmitted callback in the embed code to push a dataLayer event, and trigger your tags off that. DOM-based GTM triggers often can't see the form, especially when it renders in an iframe.

Do I need server-side tracking for a Webflow site?

If you run paid traffic, yes. Browser-only tracking on Webflow loses conversions to ad blockers and Safari's short cookie lifetimes; a first-party server-side GTM setup on your own subdomain recovers that signal and feeds accurate data to Meta CAPI, Google Ads Enhanced Conversions, and GA4.

  • webflow
  • forms
  • gtm

How accurate is your conversion tracking?

PLATFORM-REPORTEDconversionsdivided byBACKENDreal orders or leadsequalsaccuracy
TRY IT · YOUR NUMBERS
68%Broken. Something is missing entirely. Worth a free audit.
Reading guides because something is broken?Book 30 minutes with Ariful. We'll find it on the call and send a written fix list within 24 hours.
Get my free audit →
Ariful Islam
WRITTEN BYAriful IslamFounder of Tracking Consulting and a computer engineer (Diploma, BSc, MSc in CSE). 300+ clients and 1,000+ tracking projects since 2019.
About Ariful →
FREE · 30-MIN CALL · WRITTEN AUDIT IN 24H

Want us to check your accuracy for you?

We look at your setup together on the call. Within 24 hours you get a written audit and a prioritized fix list you can hand to any developer. Not a fit? We'll say so.