Blog

Framer Forms conversion tracking: 3 ways to do it

Learn three different ways to set up conversion tracking for Framer, and how to work out which one is right for you.

Framer Forms conversion tracking: 3 ways to do it

Are you struggling to track how many leads you are getting from the different marketing campaigns you are running?

It's a common problem. Most analytics tools can tell you how many website visitors you've got from different sources, but unless you've set up conversion tracking in your ad platforms and analytics tools, there's no real way of knowing how many leads you're getting.

And the weird thing is, setting up proper conversion tracking is hard.

The goal of this post is to fix that. We'll show you 3 different ways to set up conversion tracking when someone submits a Framer form on your website.

Method 1: Send a GA4 event via Google Tag Manager

Good for: Sending form submission events to Google Analytics 4, for free.

If you just want to track the number of Framer form submissions in GA4 and see which channels are driving leads, this is probably the best approach. Google Analytics doesn't need to know who submitted the form, it just needs to know that a submission happened.

Framer doesn't come with a Google Analytics integration for its forms, so you need to set something up yourself. The best way to do this is to use a Google Tag Manager recipe (which is essentially a set of pre-built assets that you upload into your Google Tag Manager account).

Step 1: Download the Framer Forms Conversion Tracking GTM Recipe

The first thing you need to do is go and download the Framer Forms GTM recipe from this page. If you're not familiar with them, GTM recipes are basically just a set of pre-built assets that you can upload into your Google Tag Manager account.

This one in particular contains the following:

  • A Custom HTML Tag that gets added to your site, listens for submissions of your Framer forms, and passes an event called ‘framer_form_submitted’ to Google Tag Manager when a successful form submission is detected
  • A Variable that captures the name of the form that was submitted (which you can use to filter your triggers to only fire on certain forms if needed).
  • A Trigger that fires when the 'framer_form_submitted' event is received, which you can use as the Trigger for any of your conversion Tags (i.e. Google Analytics 4 event, Google Ads Conversion, etc).

Step 2: Import the GTM recipe into your account

Once you've downloaded it from the page linked above, you then need to import it into your Google Tag Manager container. To do this, open your Google Tag Manager account, head to Admin, choose Import Container, select the downloaded file, and pick the Merge option (so it doesn't overwrite anything already in your container). The listener, variable and trigger are added for you automatically.

Step 3: Build a GA4 event tag

Now that the recipe is installed, you can use it as a Trigger for your conversion tags. To send a conversion to Google Analytics, create a GA4 event tag and set the Trigger to the one the recipe provides (called CE - Framer Form Submitted).

Create a GA4 event tag and fire it on the recipe's CE - Framer Form Submitted trigger

Step 4: Test it, then mark it as a Key Event in GA4

Now that everything is set up, the next thing to do is test it's working. To do this, go to one of the pages on your site that contains a Framer form and submit a test entry. Next, open GA4's Realtime report and confirm the event shows up in the recent events list.

Once it's arriving, go to Admin, then Events, find it, and toggle it on as a Key Event. That's what tells GA4 to treat it as a conversion in your acquisition and funnel reports, rather than leaving it sitting there as an ordinary event.

Pros and cons

Pros:

  • It's free, and it doesn't need a paid Framer plan or any third-party add-on.
  • The recipe captures the name of each form, so a Quote Request submission is distinguishable from a Contact Support one, and you can filter your triggers to fire on only the forms you care about.
  • It fires on a genuine, validated submission. So for instance, if someone submits the form but has made a mistake (they left a required field blank for instance), then no conversion is sent.
  • You don't need to install anything on your Framer site beyond Google Tag Manager, which you may already have running.

Cons:

  • It's a bit complicated to set up. Using a pre-built recipe helps, but you're still building the GA4 conversion tag by hand. There's definitely room for things to go wrong, and Google Tag Manager doesn't offer any support when it does.
  • It runs in the browser, so ad blockers and privacy-focused browsers like Safari and Firefox can stop the event before it ever reaches Google Analytics. That can cost you a meaningful share of your real conversions (studies say you'll miss anywhere between 30%-40% of conversions this way).
  • Getting a separate conversion count per form is extra work. The recipe gives you the form name in a variable, but you still have to build a trigger for each form you want to report on separately, or you're left with one blended total (i.e. you'll see your Quote Request form submissions and your Job Application form submissions as one blended number with no way to tell them apart).
  • It only reaches Google Analytics. It does nothing for Google Ads, Meta Ads, or anywhere else, which is what Methods 2 and 3 below are for.

Method 2: Use Google Tag Manager for Enhanced Conversions

Good for: Sending Enhanced Conversions to Google Ads.

Counting submissions the way Method 1 does is fine for Google Analytics, but it won't get you far in Google Ads.

That is because Google Ads wants to match a conversion back to the exact person who clicked your ad, and to do that you need to send information like their name, email, and phone with every conversion. This is what Google Ads calls Enhanced Conversions.

Here is how to capture that data and send it with Google Tag Manager.

Step 1: Set up a Custom HTML Tag with the form listening code

In GTM, create a new Custom HTML Tag, paste in the snippet below, and set it to fire on the All Pages trigger, with the tag firing option set to Once per event.

<script> (function () { 'use strict'; if (window.__converlyFramerMethod2Installed) return; window.__converlyFramerMethod2Installed = true; window.dataLayer = window.dataLayer || []; var SENSITIVE_AUTOCOMPLETE = { 'current-password': true, 'new-password': true, 'one-time-code': true, 'cc-number': true, 'cc-csc': true, 'cc-exp': true, 'cc-exp-month': true, 'cc-exp-year': true, 'cc-name': true, 'cc-given-name': true, 'cc-family-name': true, 'cc-additional-name': true, 'cc-type': true }; var SENSITIVE_NAME_SUBSTRINGS = [ 'password', 'passwd', 'passcode', 'cardnum', 'ccnumber', 'creditcard', 'onetimecode', 'securitycode', 'socialsecurity' ]; var SENSITIVE_NAME_TOKENS = { otp: true, cvv: true, cvc: true, ssn: true, pwd: true, pin: true }; function normaliseKey(value) { return String(value || '').toLowerCase().replace(/[^a-z0-9]/g, ''); } function isSensitiveName(name) { if (!name) return false; var lower = String(name).toLowerCase(); var norm = normaliseKey(lower); for (var i = 0; i < SENSITIVE_NAME_SUBSTRINGS.length; i++) { if (norm.indexOf(SENSITIVE_NAME_SUBSTRINGS[i]) !== -1) return true; } var parts = lower.split(/[^a-z0-9]+/); for (var p = 0; p < parts.length; p++) { if (SENSITIVE_NAME_TOKENS[parts[p]] === true) return true; } return false; } function isSensitiveField(type, autocomplete, name) { if (type === 'password') return true; if (autocomplete) { var tokens = autocomplete.split(/\s+/); for (var t = 0; t < tokens.length; t++) { if (SENSITIVE_AUTOCOMPLETE[tokens[t]] === true) return true; } } if (isSensitiveName(name)) return true; return false; } function walkForm(formEl) { var fields = []; if (!formEl || !formEl.elements) return fields; for (var i = 0; i < formEl.elements.length; i++) { var el = formEl.elements[i]; var tag = (el.tagName || '').toLowerCase(); if (tag !== 'input' && tag !== 'textarea' && tag !== 'select') continue; var type = (el.type || 'text').toLowerCase(); if (type === 'submit' || type === 'button' || type === 'reset' || type === 'image' || type === 'file' || type === 'hidden') continue; var name = el.name || el.id || el.getAttribute('data-framer-name') || el.placeholder || el.getAttribute('aria-label') || ''; if (!name) continue; if (isSensitiveField(type, (el.autocomplete || '').toLowerCase(), name)) continue; var value; if (type === 'checkbox' || type === 'radio') value = el.checked ? (el.value || 'on') : ''; else value = el.value || ''; fields.push({ name: name, type: type, autocomplete: (el.autocomplete || '').toLowerCase(), value: value }); } return fields; } function scoreField(field) { var scores = { email: 0, phone: 0, firstName: 0, lastName: 0, fullName: 0 }; var type = field.type; var ac = field.autocomplete; var key = normaliseKey(field.name); if (type === 'email') scores.email = 100; if (type === 'tel') scores.phone = 100; if (ac === 'email') scores.email = Math.max(scores.email, 90); if (ac === 'tel' || ac === 'tel-national' || ac === 'tel-local') { scores.phone = Math.max(scores.phone, 90); } if (ac === 'given-name') scores.firstName = Math.max(scores.firstName, 90); if (ac === 'family-name') scores.lastName = Math.max(scores.lastName, 90); if (ac === 'name') scores.fullName = Math.max(scores.fullName, 90); if (key.indexOf('email') !== -1) scores.email = Math.max(scores.email, 50); if (key.indexOf('phone') !== -1 || key.indexOf('mobile') !== -1 || key === 'tel' || key.indexOf('telephone') !== -1) { scores.phone = Math.max(scores.phone, 50); } if (key === 'firstname' || key === 'first' || key === 'fname' || key === 'givenname' || key.indexOf('firstname') !== -1 || key.indexOf('givenname') !== -1) { scores.firstName = Math.max(scores.firstName, 50); } if (key === 'lastname' || key === 'last' || key === 'lname' || key === 'familyname' || key.indexOf('lastname') !== -1 || key.indexOf('surname') !== -1 || key.indexOf('familyname') !== -1) { scores.lastName = Math.max(scores.lastName, 50); } if (scores.firstName === 0 && scores.lastName === 0 && (key === 'name' || key === 'fullname' || key === 'yourname')) { scores.fullName = Math.max(scores.fullName, 50); } return scores; } function identify(fields) { var best = { email: { value: undefined, score: 0 }, phone: { value: undefined, score: 0 }, firstName: { value: undefined, score: 0 }, lastName: { value: undefined, score: 0 }, fullName: { value: undefined, score: 0 } }; for (var i = 0; i < fields.length; i++) { var field = fields[i]; var scores = scoreField(field); var trimmed = (field.value || '').toString().trim(); if (!trimmed) continue; for (var category in scores) { if (scores[category] > best[category].score) { best[category] = { value: trimmed, score: scores[category] }; } } } var result = {}; if (best.email.value && (best.email.score >= 90 || best.email.value.indexOf('@') !== -1)) { result.email = best.email.value; } if (best.phone.value && (best.phone.score >= 90 || (best.phone.value.match(/\d/g) || []).length >= 4)) { result.phone = best.phone.value; } if (best.firstName.value) result.firstName = best.firstName.value; if (best.lastName.value) result.lastName = best.lastName.value; if (best.fullName.value && !result.firstName && !result.lastName) { var parts = best.fullName.value.split(/\s+/); if (parts.length === 1) { result.firstName = parts[0]; } else { result.firstName = parts[0]; result.lastName = parts.slice(1).join(' '); } } return result; } function isFramerForm(formEl) { if (!formEl) return false; if (formEl.hasAttribute && formEl.hasAttribute('data-framer-name')) return true; var cls = typeof formEl.className === 'string' ? formEl.className : ''; if (/(^|\s)framer-[A-Za-z0-9_-]+/.test(cls)) return true; try { if (formEl.closest && formEl.closest('[data-framer-component-type]')) return true; if (formEl.closest && formEl.closest('[data-framer-name]')) return true; } catch (e) { // .closest not available, fall through } return false; } function handleSubmit(event) { try { var formEl = event.target; if (!formEl || (formEl.tagName || '').toLowerCase() !== 'form') return; if (!isFramerForm(formEl)) return; var formName = formEl.getAttribute('data-framer-name') || formEl.id || ''; var user = identify(walkForm(formEl)); var payload = { event: 'framer_form_submitted', form_name: formName }; if (user.email) payload.email = user.email; if (user.firstName) payload.first_name = user.firstName; if (user.lastName) payload.last_name = user.lastName; if (user.phone) payload.phone = user.phone; window.dataLayer.push(payload); } catch (e) { // Never throw back into Framer's own form handling. } } if (typeof document !== 'undefined' && document.addEventListener) { document.addEventListener('submit', handleSubmit, true); } })(); </script>

The above code does a bunch of things:

  • Watches for a real Framer form being submitted. Framer's forms are built with React, and Framer's own handler intercepts the browser's normal submission and stops it from going through the usual way (so a script that listens for the submission the normal way would never see it happen). This one listens earlier, in the part of the browser's event flow that runs before Framer gets the chance to intercept it, so it catches every submission Framer itself does.
  • Confirms it's actually a Framer form, not just any form on the page. It checks for the markers Framer leaves on its own forms (an internal name attribute, a distinctive class, or being nested inside a Framer form component) before doing anything else.
  • Works out which field is which, even when Framer hasn't named it. A lot of real, published Framer forms don't have a name or id set on their fields the way forms from other website builders do. So this snippet falls back through whatever Framer did set, its internal field name, the placeholder text, or the field's aria-label, to figure out which one is the email, the name, and the phone.
  • Skips anything sensitive. Passwords, card numbers, CVV fields, one-time codes, and similar sensitive fields are checked and dropped before the code ever stores or reads them.
  • Sends the lead's details to the dataLayer. Once a genuine submission goes through, the code pushes a framer_form_submitted event onto the dataLayer, along with the lead's name, email, and phone, ready to be used as the trigger for your conversion tags.

This snippet has been extensively tested and addresses a number of issues that other scripts on the internet don't, including:

Issue 1: A normal script never sees a Framer form get submitted at all. Framer's own React code intercepts the browser's default form handling and stops it before it can go through, and a script that listens for the submission the ordinary way misses it completely. So if you tried to use Google Tag Manager's built-in form submission trigger on a Framer site, it will never fire, and you'll have no idea why your conversion count is stuck at zero. This snippet listens earlier in the browser's event flow, at the point where it can still see the submission before Framer's own code gets to it.

Issue 2: A lot of real Framer forms don't name their fields at all. Most scripts identify the email field by its name attribute, something like email or user_email. But Framer's own form builder frequently leaves fields with no name and no id set, especially on forms built visually rather than with custom code. A script that only trusts the name attribute would push an event with no email, no name, and no phone, which is useless for Enhanced Conversions. This snippet falls back through the field's placeholder text and its label, so it can still identify the field correctly even when Framer hasn't named it.

Issue 3: Duplicate fields can end up sharing a name. Framer's builder makes it easy to duplicate a field, and if the copy isn't renamed properly, you can end up with two genuinely different fields, like a Company field and a Phone field, technically sharing the same name. A script that trusts a name match on its own can grab the wrong value and send a company name to Google Ads under the phone field, which drags down your match rate without any obvious explanation. This snippet only accepts a weak, name-based match when the value actually looks right, an email has to contain an @ symbol and a phone number has to contain several digits, so a mismatched field gets skipped instead of sent through as bad data.

As you can probably tell from reading the above, this isn't a snippet we put together just for this article. It's the same detection logic Converly runs in its own Framer integration to track form submissions and send them to ad platforms and analytics tools, and it has been extensively tested against a real, published Framer site (plus used by our customers to detect thousands of form submissions).

Step 2: Create Data Layer Variables for the captured data

Before you can use the data the snippet about just sent into the dataLayer, you need to tell Google Tag Manager how to pull the lead's details out so they can be sent to Google Ads, Meta Ads, and so on. A Data Layer Variable is what does that.

To set one up, go to the Variables section in GTM, click New under User-Defined Variables, choose Data Layer Variable as the type, and give it a name.

Create Data Layer Variables for email, first name, last name and phone

Here are the four you need.

Variable 1:

  • Name: Email
  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: email

Variable 2:

  • Name: First Name
  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: first_name

Variable 3:

  • Name: Last Name
  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: last_name

Variable 4:

  • Name: Phone
  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: phone

Step 3: Create a Custom Event trigger

Now that you have the Variables set up to pull the data out of the dataLayer, you need to tell Google Tag Manager when to use them. That is what a Trigger is for. It watches for the message the snippet pushes into the dataLayer when a Framer form is submitted, and tells your conversion tags to fire when it arrives.

To set this up, navigate to the Triggers section, click New, choose the Custom Event type, and set the event name to framer_form_submitted. It has to match what the code pushes exactly.

Create a Custom Event trigger in Google Tag Manager

Step 4: Turn on Enhanced Conversions in Google Ads

Before any of this can reach Google Ads, Google Ads itself needs to be set up to accept it. Head to your Google Ads account, go to Goals, then Conversions, then Settings, and turn on Enhanced Conversions.

Turn on Enhanced Conversions in your Google Ads settings

When it asks how you want to implement it, choose Google Tag Manager. This gives Google Ads permission to receive and use the customer data you're about to send.

Step 5: Create your Google Ads conversion tag and map the data

Now it's time to put the pieces together. Jump back into GTM, go to Tags, select New, choose Google Ads User Provided Data Event as the tag type, and map the variables from Step 2 to the fields it asks for.

Map your Data Layer Variables in the Google Ads User Provided Data tag

Then set it to fire on the Custom Event trigger you created in Step 3.

Set the Google Ads tag to fire on the Custom Event trigger

Once that's done, publish your GTM container. With some luck, you'll have set up a Tag to listen for Framer submissions and send an event to the dataLayer, some Variables to pull the information out of it, a Trigger to watch for the event, and a Tag that sends a conversion when it arrives.

To check it's working, open GTM Preview mode, submit the Framer form on your site, and confirm the framer_form_submitted event arrives with the email and name filled in.

Pros and cons

Pros:

  • It's free, and you probably already have Google Tag Manager installed on your site, so that's a start.
  • Unlike Method 1, this method captures the name, email, and phone and sends them to Google Ads, or wherever else you configure them to be sent. This is the minimum needed to enable Enhanced Conversions in Google Ads, and it will help your match rate on platforms like Meta Ads and LinkedIn Ads.
  • It only counts submissions that pass your form's required-field validation, so someone who leaves a required field blank never triggers a conversion (unlike a raw click-based trigger).

Cons:

  • There are a lot of moving parts. Between the GTM tag, the trigger, four variables, the Google Ads settings, and the conversion tag itself, there is plenty to get wrong, and there are no error messages telling you which piece failed if it goes bad.
  • It still sends the conversion through the visitor's browser, so ad blockers and privacy-focused browsers can stop it working properly. Expect to lose 30% or more of your conversions this way.
  • Framer never sends a confirmation message back to the browser when the form submission has been received by it's servers. So this method counts conversions when the browser accepts a valid submission, not once Framer's backend confirms it received it. In practice, this rarely matters, but on the rare occasion the form submission is rejected by Framer's servers, it still gets counted.
  • It still misses the data Google and Meta value most. Name, email, and phone are just the minimum for Enhanced Conversions. It doesn't capture the Google Click ID, the Facebook Click ID, the visitor's IP address, the user agent, and so on. These are the exact-match identifiers ad platforms really want you to send, because a click ID (for instance) can tie the conversion back to the original ad click with 100% accuracy.

That last point is the one that pushes a lot of people to Method 3.

Method 3: Use Converly

Best for: Sending proper, server-side conversions to Google Ads, Meta Ads, ChatGPT Ads, LinkedIn Ads, and more, without having to do complicated configuration in Google Tag Manager.

Converly is a purpose-built conversion tracking tool that makes it easy to send server-side conversions to Google Ads, Meta Ads, LinkedIn Ads, ChatGPT Ads, and more, whenever a visitor submits a Framer form on your site.

Converly flow builder connecting Framer to Google Analytics, Google Ads, and Meta Ads

As you can see above, setup is genuinely simple. You just pick a trigger (like a Framer form submission) and then pick one or more actions, like sending a conversion to Google Ads or to Meta Ads. That is all there is to it. No custom code, and no Tag Manager triggers or variables to piece together.

From there, you just paste the Converly code on your site, and it does the rest. It watches for Framer form submissions, pulls out the name, email, and phone, and sends them to Converly's own servers, which pass them on to Google Ads, Meta Ads, and wherever else you have connected. Because this forwarding step happens server-side, ad blockers and privacy-focused browsers like Safari can't get in the way.

But here is the part that matters most if you're running ads on Google, Meta, ChatGPT, and so on. Alongside the name, email, and phone, Converly also grabs the Google and Meta click IDs (GCLID and fbclid), Meta's browser cookies, the visitor's IP address, their user agent, and more, and sends it all with each conversion. Those are the signals platforms like Google and Meta really want, and having the full set is what pushes your match quality from weak to strong (On Meta, this will likely lift your Event Match Quality from around 3 up to a 9 or 10).

Converly also gives you a full conversion log, so you can see every lead that submitted one of your Framer forms, what data was captured, what was sent to each ad platform, and whether it landed.

Converly's conversion record showing the marketing attribution, click IDs, and browser data captured with each lead

Pros and cons

Pros:

  • Setup is genuinely easy, with no code to write or maintain, and no complex configuration to fight with. You can even connect your favorite AI tools (Claude, ChatGPT, etc) and just say ‘Set up conversion tracking in Framer Forms’ and it will do it all for you!
  • Captures the click IDs, cookies, IP, and user agent, and sends them along with every conversion. This dramatically lifts match quality in the ad platform. For example, if you are running Meta Ads then you would typically go from an Event Match Quality score of 3 or 4 up to a 9 or 10 with Converly.
  • Sends the conversion server-side, so ad blockers and privacy browsers don't stop them from reaching your ad platforms. You will likely record 30% more conversions using Converly than using Google Tag Manager.
  • Supports conditional logic, so you can send specific conversions when certain forms are completed on your site. For example, you could only fire a conversion when the Quote Request form is submitted, not the Contact Support form, even though they are both built with Framer forms.
  • Lets you log in to Google Ads, Meta Ads, and so on within the platform and just pick the conversion you want to fire. No hunting around for account IDs or conversion IDs.
  • Gives you a full conversion log where you can see every lead that submitted your Framer form, what data was captured, whether it made it to your ad platforms, and more.
  • You get support from a team of experts who will happily help you get set up and troubleshoot issues. Good luck getting that kind of help from Google if you're using GTM.

Cons:

  • It's a paid tool after the free trial. Plans start at $19 per month.

What not to do

So far we have covered the three best approaches to conversion tracking for Framer, and when to use each one. But it's also worth knowing which approaches to avoid, because a few of the most common DIY approaches to Framer conversion tracking can result in you sending seriously bad data back to your ad platforms and analytics tools. Here is what to steer clear of:

Tracking thank-you page visits

This is one of the most common DIY approaches, and it's one of the worst. The idea is to send people to a dedicated thank-you page after they submit, then count visits to that page as conversions. It sounds clean, but it goes wrong in several ways, and for Framer specifically, it often doesn't work at all.

By default, Framer doesn't send anyone to a thank-you page. A successful submission shows a confirmation message inside the form itself, on the same page, unless you have specifically gone into the form's settings and turned on a redirect. So if you build a new form and accidentally forget to enable the redirect, you'll get 0 conversions from anyone who submits the form.

It fires for people who never submitted anything. A thank-you page is just a URL, and a URL can be reached without ever submitting a form. People can land on it from their browser history or autocomplete, or stumble on it through Google if it ever gets indexed. Every one of those gets counted as a conversion, even though the form was never submitted.

It can double count genuine submissions. If a lead refreshes the thank-you page by accident, a second conversion gets recorded. If they hit the back button and land on it again, that's a third. One real lead can generate several conversions, which inflates your total count.

Match rates would be poor. A thank-you page has no idea who submitted the form, so there is no way to capture the lead's name, email, phone, Google Click ID, Facebook Click ID, etc and that data back to the ad platforms. And without that, sending conversions to ad platforms is kind of pointless, since the ad platforms don't have the data they need to match the conversion back to a real person and the ad they clicked.

The root problem here is that a thank-you page visit is a proxy for a conversion, not the conversion itself. The three methods above all fire on a genuine Framer form submission instead, so they are much more accurate.

Relying on the default form submit trigger in Google Tag Manager

If you're setting up conversion tracking in Google Tag Manager, the obvious first thing to do is use the built-in Form Submission trigger, which listens for the browser's native form submit event.

On a Framer site, the Form Submission doesn't fire at all. Framer's forms are React components, and Framer's own code intercepts the browser's default submission handling before a normal, ordinary trigger like this one ever gets to see it. You would set the whole thing up, believe it's working, and watch your conversion count sit at zero with nothing telling you why.

Always listen for Framer's actual submission signal instead, the way Methods 1 through 3 above do.

Using GA4's automatic form tracking (Enhanced Measurement)

GA4 ships a built-in Enhanced Measurement feature that claims to detect form submissions automatically, with no setup required.

For the same reason the built-in GTM trigger fails on Framer, Enhanced Measurement fails as well. It relies on the same kind of generic browser-level detection, which Framer's React handler gets in the way of. Turning it on and assuming your Framer forms are already covered is one of the more common mistakes we see, and it leaves you with a Realtime report showing nothing, even while real leads are coming through the form.

So if you want accurate conversion tracking on a Framer site, use one of the methods mentioned above instead.

Firing the conversion on the submit button click

Some setups we come across send the conversion as soon as someone clicks the submit button, typically using a ‘click trigger’ in GTM aimed at the button itself.

But a click isn't a submission. If the visitor has left a required field blank, Framer's native validation blocks the actual submission and shows an inline error, and then you've recorded a conversion that never happened. Then when the visitor fixes the issue and clicks submit again, a second conversion gets counted for the same lead. Not good.

So which method should I use?

Here's how the three ways to track Framer Forms conversions compare.

MethodBest forSends conversions toData sent
Method 1. GA4 event built in Google Tag ManagerSending submissions to Google Analytics, if you're happy using the free GTM recipe and building the conversion tag yourselfGoogle Analytics 4The submission event only
Method 2. Enhanced Conversions through Google Tag ManagerSending to Google Ads, if fiddly configuration doesn't put you offGoogle AdsName, email and phone
Method 3. ConverlySending to any ad platform, with the least setup workGoogle Ads, Meta Ads, LinkedIn Ads, ChatGPT Ads, GA4 and moreName, email and phone, plus the GCLID, the Meta click ID, IP address and user agent

Wrap up

Conversion tracking often gets overlooked, but it has a real effect on how well the rest of your advertising performs. Do it right, and Google's and Meta's algorithms build a picture of your ideal customer and put your ads in front of more people who look like them. Do it wrong, and you keep spending money advertising to people who were never going to buy anything.

The benefit isn't just theoretical either, as both platforms have published figures on it. Google reports that businesses moving to proper server-side conversion tracking see conversions climb by 19% on average. Meta reports a similar 23% increase from doing the same on its platform.

Converly gives you all the benefits of server-side tracking without any of the hard parts. You just pick a trigger (a Framer form submission), choose which platforms should get the conversion, and the setup is done.

Best of all, Converly is free to start with, and most people are fully up and running in under 10 minutes. Start your 14-day free trial today.

About the author

Aaron Beashel
Aaron Beashel

Aaron is the founder of Converly. With over 15 years of experience in digital marketing and SaaS, he's passionate about helping businesses track and optimise their ad conversions.

Start your free trial

Easily send conversions to your ad platforms and analytics tools.
No code required.