
Author Name
Emtiaz Hossain
JotForm is usually embedded one of two ways, as an iframe or as inline source code pulled directly into the page, and each embed method needs a completely different tracking approach because the form literally isn't running in the same document context in the iframe case.
When JotForm is embedded as an iframe, the form runs in a separate document, tracking scripts on the parent page can't reach into it directly. JotForm instead posts a message event across the iframe boundary on successful submission.
<script>
window.addEventListener('message', function (event) {
if(event.origin.includes('jotform.com') && event.data.action === 'submission-completed') {
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'jotForm_submit_v2',
formId: event.data.formID
});
}
});
</script>This only fires once JotForm's own backend confirms the submission actually went through, since the message comes from JotForm's servers via the iframe, not from anything client-side JavaScript could fake or misfire on. The tradeoff is that the message payload is minimal, mostly just a form ID and the completion action, not the actual field values, since those live inside the iframe's own document and aren't exposed across the origin boundary by default.
When JotForm's "Source Code" embed option is used instead (form HTML pulled directly into the page rather than iframed), the form is a normal same-origin form and can be tracked with a standard submit listener.
<script>
(function() {
document.addEventListener('submit', function(event) {
var form = event.target.closest('form[action^="https://submit.jotform.com"]');
if(form) {
var formData = new FormData(form);
var formInputs = {};
formData.forEach(function(value, key) {
formInputs[key] = value;
});
delete formInputs['jsExecutionTracker'];
delete formInputs['validatedNewRequiredFieldIDs'];
delete formInputs['simple_spc'];
delete formInputs['submitSource'];
dataLayer.push({
event: 'jotForm_submit_v2',
formId: formInputs.formID,
inputs: formInputs
});
}
});
})();
</script>Two things worth noting in this version specifically:
It targets the form by its actual submit action, not an ID. form[action^="https://submit.jotform.com"] matches any JotForm source-embedded form regardless of its DOM ID, useful since JotForm's generated form IDs aren't always predictable or stable across re-embeds.
Internal JotForm tracking fields are explicitly stripped. jsExecutionTracker, validatedNewRequiredFieldIDs, simple_spc, and submitSource are JotForm's own internal bookkeeping fields, not real user data, and are deleted from the object before it reaches the dataLayer so they don't clutter reporting or get mistaken for actual form inputs.
Check the embed code JotForm generated for the site: an <iframe> tag means Method 1, a block of raw form HTML (typically starting with a <form> tag pointing at submit.jotform.com) means Method 2. Using the iframe pattern on a source-embedded form (or vice versa) means the event listener never fires, since it's listening for a signal that embed type never sends.
jotForm_submit_v2. For iframe embeds, only formId is available at the dataLayer level, if field-level data is needed, that requires JotForm's own webhook/API integration rather than client-side tracking. For source-code embeds, map fields directly off inputs.*.Discussed this on LinkedIn
Found this helpful?
Drop a comment on the LinkedIn post – I read every reply and love connecting with tracking folks.
Book a free 30-minute call. I will review your current tracking setup, show you where conversions are leaking, and tell you exactly what I would fix.
A full GA4/GTM/Google Ads audit on a UK Shopify store spending £18,000+/month found duplicate conversion tags inflating reported purchases 2x, a dropped GCLID at checkout, and misconfigured variables silently degrading Enhanced Conversions. Here's the audit, the fixes, and the migration off a legacy GTM setup before Shopify's deprecation deadline.
How a UK marketing agency's LinkedIn Insight Tag upgrade turned into a full conversion audit, catching duplicate tags and stale triggers across eight lead-gen conversions.
Duplicate form conversions in GA4 are usually a reload firing the same tag twice, not a real second submission. Here's the Custom JavaScript variable that catches it, plus other common causes to rule out.