How to Track JotForm Iframe Conversion Events in Google Analytics
Capture JotForm iframe submissions with postMessage and Google Tag Manager, then push them to GA4 as conversions. A step-by-step operator's guide.
A JotForm embedded in an iframe is a black box to your analytics. The form lives on JotForm's domain, your page lives on yours, and the browser's same-origin policy makes sure the two never share notes. So someone fills out your form, hits submit, gets the thank-you screen — and Google Analytics records absolutely nothing. No conversion. No event. The lead happened in a room your tracking can't see into.
Here's the short version: to track JotForm iframe conversion events in Google Analytics, you send a postMessage from inside the form on submit, catch it with an event listener in Google Tag Manager, push it to the data layer, and fire a GA4 event off the back of it. That's the whole signal path — from submit to closed-loop conversion — and it takes about twenty minutes once you know where the gotchas are.
We've wired this exact pattern more times than I'd care to count. Below is the version that actually works, gotchas included.
Table of contents
- Why iframe forms break tracking
- Step 1: Understand post messages
- Step 2: Send a post message on submit
- Step 3: Receive the message in the parent window
- Step 4: Push the event to Google Analytics
- Why this approach beats the alternatives
- FAQ
Why iframe forms break tracking
An iframe is a window into another website. JotForm renders your form on their origin, then drops it into your page through an <iframe> tag. For security, the browser won't let one origin read or script the other — which is exactly the protection you want everywhere except here, where you genuinely need to know a form got submitted.
So your GA4 sits on the parent page, blind to anything happening inside that window. The submit fires, JotForm's success screen loads, and your conversion count stays flat. If you've ever stared at a form that's clearly getting filled out and wondered why analytics shows zero conversions, this is usually why.
The fix isn't a hack. It's the Web Messaging API doing precisely the job it was built for: a controlled, deliberate handoff of data across that boundary. You decide what crosses, and nothing else does.
Step 1: Understand post messages
Post messages are part of the Web Messaging API{target="_blank" rel="noopener"}, which allows secure communication between different browsing contexts — windows or iframes — whether they share the same origin or not. It's the sanctioned way to pass data or instructions between separate parts of a web page that otherwise can't touch each other.
That last part is the whole point. The iframe can't reach into your page and the page can't reach into the iframe, but both can send each other a message and listen for one. One side posts, the other receives. Think of it as sliding a note under the door instead of trying to walk through the wall.
Step 2: Send a post message on submit
Inside your embedded iframe — your JotForm — you fire a post message when a specific action happens. After a user successfully submits the form, send a message like this:
window.parent.postMessage({ action: 'submission-completed', formID: '213215413867050' }, '*');
That call says: "Hey parent window, the form with this ID just got submitted." The formID lets the parent confirm it's the right form and not some other iframe shouting into the void. (Swap 213215413867050 for your own form's ID — JotForm assigns it, and you'll find it in the form's URL.)
A note on that '*' at the end. It's the target origin, and '*' means "send to any parent, I don't care who's listening." Fine for getting started. For production you'd ideally lock it to your own domain so the message can't be intercepted, but plenty of working setups ship with '*' and live to tell the tale.
Step 3: Receive the message in the parent window
In the parent window — the page where the iframe is embedded — you set up an event listener to catch the post message. This is where Google Tag Manager earns its keep. We use a Custom HTML Block to drop in the listener; once we have the data, we create a custom event and build a trigger off it to push the data anywhere we want — GA4, the Meta pixel, you name it.
Here's the listener:
window.addEventListener('message', function(event) { // Check if the event data matches the expected format if (event.data && event.data.action === 'submission-completed' && event.data.formID === '213215413867050') { // Perform actions in response to the submission-completed event console.log('Submission completed for form with ID:', event.data.formID); // Push a data layer event for form submission dataLayer.push({ 'event': 'form_submit_completed', 'formID': event.data.formID, 'timestamp': new Date().toISOString() // Add more data as needed }); } });
Read what it actually does. It listens for any message, then checks three things before it trusts the payload: that there's data, that the action is submission-completed, and that the formID matches the one you expect. Only when all three line up does it push a form_submit_completed event into the data layer.
Don't skip that validation. A page is full of scripts firing message events for their own reasons — chat widgets, embeds, A/B tools — and if you push a conversion every time anything messages your window, your numbers turn to confetti. The formID check is the bouncer at the door. This is the same lesson we preach about UTM formats and how they impact your marketing pipeline: one wrong value and the whole attribution chain breaks downstream.
Step 4: Push the event to Google Analytics
Now the payoff. Once form_submit_completed lands in the data layer, GTM can act on it. You create a Custom Event trigger in GTM that listens for form_submit_completed, then attach a GA4 Event tag to that trigger.
In the classic version of this we'd use the ga tracking function to send a custom event — say, event name Form, category Submission, label JotForm in Iframe. In GA4 the shape changes but the idea is identical: name the event (generate_lead or form_submit are sensible choices), pass along whatever parameters you care about, and mark it as a key event so it counts as a conversion. Customize those names and parameters to fit your own tracking — the mechanism is the same either way.
That's the closed loop. Submit fires in the iframe, message crosses the boundary, GTM catches it, data layer carries it, GA4 records the conversion. The black box has a window in it now, and your tracking can finally see the lead it always knew was happening.
Why this approach beats the alternatives
You could bolt on a third-party JotForm integration, or poll for a thank-you-page URL change, or stitch together a Zapier workflow and hope it stays connected. We've inherited stacks built on all of those, and they tend to age like milk — brittle, opaque, and impossible to debug when the numbers drift.
The postMessage-to-GTM path is different because it's yours. The signal originates from the form's own submit event, travels through tooling you control, and lands in analytics you can audit. When attribution matters — and it always matters if you're spending real money on acquisition — you want the path you can trace end to end, not the one that's a black box wrapped in another black box.
If you're standing up GA4 conversions more broadly, it pairs naturally with setting up conversion tracking in Google Analytics and getting your Google Tag Manager and Webflow integration wired correctly from the start. And when you want this built right the first time instead of debugged at 11pm on a Friday, that's the work we do — tracking and analytics that close the loop from click to closed-won.
FAQ
Why doesn't Google Analytics track my JotForm submissions automatically? Because the form lives inside an iframe on JotForm's domain, and the browser's same-origin policy stops your page from seeing what happens inside it. GA4 sits on the parent page with no visibility into the form, so the submit fires invisibly. The postMessage technique is what bridges that gap.
Do I need Google Tag Manager for this, or can I do it with raw code? You can do it with raw JavaScript, but GTM makes it far cleaner. You drop the listener into a Custom HTML tag, push to the data layer, then fire GA4 (and any pixels) off a single Custom Event trigger — no redeploys, no digging through site code every time you tweak a parameter.
Is the '*' target origin safe to use?
It works, but it's the loose setting — it broadcasts the message to any listening parent. For production, replace '*' with your specific domain so the message can only be read by your own page. It's a small change that closes a real gap.
How do I find my JotForm form ID?
It's the long number JotForm assigns each form, visible in the form's edit URL and embed code. In the examples above it's 213215413867050 — swap in your own. Matching on the form ID is also how the listener avoids reacting to unrelated message events from other scripts on the page.
Can I send the same submission to Meta or other pixels too?
Yes, and that's the main reason to route everything through the data layer. Once form_submit_completed is in the data layer, you can attach as many tags as you like to its trigger — GA4, the Meta pixel, LinkedIn, your CRM — all firing off the one clean event instead of duplicating tracking logic.
Will this double-count if the user submits twice? Each genuine submit fires its own message, so two submissions log two events — which is usually correct. If you want to dedupe, add a flag in the data layer after the first push or include a unique submission token in the message payload and check it before pushing again.