HubSpot
How to Track HubSpot Form Submissions in GA4
HubSpot forms load in an iframe GA4 can't see into. Here's the callback-based way to fire a real GA4 event when one gets submitted.

Key Takeaways
- The iframe is the reason this needs a build at all: no GA4 click trigger on the parent page can see a submit happening inside a HubSpot form's iframe.
- HubSpot posts a message to the parent window on submission, with event.data.type of hsFormCallback and event.data.eventName of onFormSubmitted; that message is what Google Tag Manager listens for.
- This works on the standard embed. Converting a form to raw HTML to dodge the iframe needs the legacy form editor plus a Marketing Hub or Content Hub Professional or Enterprise subscription, so it isn't available to every portal, and the callback method doesn't need it.
- A submission only joins back to its GA4 session if a hidden field carrying the GA4 client ID is added to the form, written at render time before the visitor submits; it is not retroactive, so submissions before the field existed carry no client ID.
- GA4 no longer calls a conversion a Goal. Since March 2024 that concept is called a key event across GA4's admin screens and reports.
Connecting Google Analytics 4 to HubSpot gets you page views and traffic sources. It does not get you the one action that usually matters most: a visitor filling out a form. This guide covers that specific gap, why it exists, and how to close it with Google Tag Manager.
Why This Needs a Separate Build
A standard HubSpot form embed renders inside an iframe. GA4's usual method for catching an interaction, a click trigger watching the page, can't see anything happening inside that iframe, which is why a form submission never turns into a GA4 event on its own, no matter how correctly the base integration is set up.
HubSpot's own workaround is a message, not a DOM event. On submission, it posts a message to the parent window: event.data.type of hsFormCallback, with event.data.eventName of onFormSubmitted. That message is visible to the parent page, which is the hook the rest of this setup uses.
One route some guides suggest, converting the form to raw HTML so it stops rendering inside an iframe at all, is gated twice over: it only works on a form built in HubSpot's legacy form editor, since the current default editor has no raw-HTML option, and rendering it that way needs a Marketing Hub or Content Hub Professional or Enterprise subscription on top of that. A Starter portal has no version of that route available. The callback method below works on the standard embed regardless of tier, which is why it's the one to build first.
How to Track Form Submissions in GA4
You'll need edit access to Google Tag Manager and a GA4 property already set up for your domain.
Step 1: Listen for the HubSpot Callback
In Google Tag Manager, create a new Custom HTML tag that adds a listener for the browser's message event, checks for event.data.type === 'hsFormCallback' and event.data.eventName === 'onFormSubmitted', and pushes an event into the dataLayer when it matches, for example dataLayer.push({event: 'hubspot_form_submit'}). Set the tag to fire on all pages where a HubSpot form can appear.
Step 2: Create a Custom Event Trigger
Under Triggers, create a new trigger of type Custom Event, and set the event name to match what you pushed in Step 1 (hubspot_form_submit).
Step 3: Fire a GA4 Event Tag
Create a Google Analytics: GA4 Event tag, pointed at your GA4 configuration tag, with an event name such as hubspot_form_submission. Set it to fire on the Custom Event trigger from Step 2.
Step 4: Mark It a Key Event in GA4
Publish the container, then submit a test form to confirm the event fires (GA4's DebugView or realtime report is the fastest way to check). Once it's confirmed, go to GA4's Admin > Events, find the event, and mark it as a key event. Note the terminology: GA4 retired the word "Goal" for this in March 2024, so if a guide or a colleague still says Goal, they mean what GA4 now calls a key event. Marking it lets the submission be used as a conversion in reports and as the basis for an audience.
One practical note before you move on: name the dataLayer event and the GA4 event something specific to what the form actually does, rather than a generic form_submit reused across every form on the site. A single shared event name makes every form indistinguishable in GA4 reporting, which defeats the point of setting this up in the first place. If the site runs several forms, either fire a distinct event per form or push a form-name parameter alongside the event so the two can be split apart later.
Join the Submission Back to Its Session
A form submission on its own tells you a form was filled out, not who filled it out or what GA4 session they arrived on. HubSpot's visitor cookie and GA4's client ID are separate identifiers, and nothing bridges them automatically. The fix is a hidden field on the form carrying the GA4 client ID, written at render time (on HubSpot's onFormReady callback, before the visitor has done anything) rather than at the moment of submission. Two things make this worth setting up early: it isn't retroactive, so a submission taken before the field existed has no client ID and no documented way to recover one, and it depends on the tracking cookie actually reaching HubSpot, which won't happen if the form submits through a server-side proxy that strips it.
Read the Result Honestly
Once this is live, expect the numbers to include a consent gap, not just a tracking gap. A visitor who submits a form without accepting cookies, whether they declined the banner or simply never clicked Accept, is recorded as direct traffic rather than attributed to the channel that actually brought them. If the banner hasn't been scoped to specific countries, it defaults to showing every visitor, so this shows up in regional data more often than it does on a site built and tested for a single market.
That's also where the payoff is for a team running sales, marketing and service off one pipeline: a form event that's actually attributed, actually joined to a GA4 session, and actually distinguishable from a submission HubSpot never got consent to track, is the difference between a RevOps reporting view leadership trusts and one that quietly undercounts every visitor who ignored a cookie banner.
If your team is setting this up as part of a wider reporting rebuild rather than one tag at a time, that's RevOps work, and it's worth scoping the whole attribution chain rather than patching it form by form.
Sources
Frequently Asked Questions
Why doesn't a HubSpot form submission just show up in GA4 automatically?
Because of how the form is rendered. A standard HubSpot form embed loads inside an iframe, and a click or submit event happening inside that iframe is invisible to a trigger set up on the parent page, which is how GA4 and Google Tag Manager normally detect this kind of interaction. HubSpot works around its own iframe by posting a message to the parent window when the form is submitted, and that message is what a tag manager setup has to listen for instead.
What is the hsFormCallback message and why does it matter for GA4 tracking?
It's the signal HubSpot sends to the parent page on a form event. On submission, the message carries event.data.type of hsFormCallback and event.data.eventName of onFormSubmitted. A Custom HTML tag in Google Tag Manager that listens for that specific combination, then pushes an event into the dataLayer, is what makes the submission visible to a Custom Event trigger and, from there, to a GA4 event tag.
Can I just convert the form to raw HTML to avoid the iframe problem?
Only on some portals, and it isn't the easier route. Converting to raw HTML requires the form to have been built in HubSpot's legacy form editor, since the current default editor can't set a form as raw HTML at all, and rendering it that way also needs a Marketing Hub or Content Hub Professional or Enterprise subscription. A Starter portal has no version of that workaround available on any form. The callback-based method works on the standard embed regardless of tier, which is why it's the one worth setting up first.
Does tracking form submissions in GA4 tell me which HubSpot contact submitted?
Not directly, and not automatically. HubSpot's visitor cookie and GA4's client ID are different identifiers. To join a submission back to its GA4 session, a hidden form field carrying the GA4 client ID has to be added and populated at render time, before the visitor ever submits. It isn't retroactive: a submission taken before that field existed has no client ID attached and there's no documented way to backfill it, so it's worth adding on day one rather than after the fact.
Why is HubSpot showing more direct traffic than I expect once form tracking is live?
Likely a consent effect rather than a tracking bug. Where a visitor converts on a form without accepting cookies, whether they actively declined or simply never clicked Accept, HubSpot records the resulting contact as direct traffic rather than attributing them to the channel that brought them. If the consent banner hasn't been scoped to specific countries, it's shown to every visitor by default, so this shows up on regional traffic more often than teams expect.
Revenue Operations
Do Sales, Marketing and Service Actually Share One Pipeline?
When each team runs its own process, revenue reporting stops matching reality. We align lifecycle stages, handoffs and attribution across HubSpot's hubs so leadership sees one accurate pipeline, not three conflicting ones.
Related Articles

How to Choose a HubSpot Agency in Dubai: 10 Questions and the Answers to Listen For
Ten questions to put to a HubSpot agency in Dubai, and what a good answer sounds like on Arabic delivery, data residency, imports and the compliance review.

