Skip to main content

HubSpot Form GTM Event Listener

Track HubSpot form submissions and pre-submission data in Google Tag Manager. Capture form GUID, field data, and fire GA4, Google Ads, and Meta Pixel on hubspot-form-success.

hubspotformsgtmga4lead-generationpostmessagecrm

Event fired

hubspot-form-data

HubSpot Form

Overview

HubSpot forms are embedded via an iframe and communicate with the parent page using the browser's window.postMessage API. This listener intercepts those cross-origin messages and extracts two distinct events: a pre-submission event that captures all form field data, and a success event that confirms the submission completed.

Events fired: hubspot-form-data (pre-submit with fields), hubspot-form-success (confirmed submit) Credit: 3WhiteHats (modified by DumbData)

Why Use This Listener

HubSpot forms render inside an iframe. GTM's standard Form Submit trigger cannot cross iframe boundaries, meaning without this listener, HubSpot form conversions are completely untrackable in GTM. This listener bridges the iframe gap using the PostMessage API that HubSpot uses internally.

Without ListenerWith Listener
HubSpot form iframes are invisible to GTMFull visibility into form lifecycle
Zero conversion dataSubmit confirmed + field data captured
No form identificationhs-form-guid uniquely identifies each form
Can't fire GA4 or pixels on HS formsAny tag can fire on HS form success

Common Use Cases

  • CRM lead tracking, Match HubSpot form submissions to GA4 sessions for attribution
  • Google Ads conversion tracking, Import HubSpot form fills as Google Ads conversions
  • Multi-form attribution, Track which HubSpot form (demo, contact, newsletter) drives pipeline
  • Enhanced conversions, Capture hashed email for Google's Enhanced Conversions
  • LinkedIn Lead Gen correlation, Compare HubSpot form leads with LinkedIn campaign data

How It Works

HubSpot sends postMessage events from the embedded iframe to the parent window. The listener uses window.addEventListener('message', ...) to intercept these messages and filters for HubSpot-specific message types.

Two key message types:

  • hsFormCallback with type onBeforeFormSubmit, fires before submission, contains field data
  • hsFormCallback with type onFormSubmitted, fires after successful submission
mermaid
sequenceDiagram
    participant U as User
    participant HS as HubSpot iframe
    participant PW as Parent Window
    participant DL as dataLayer
    participant GTM as GTM

    U->>HS: Submits form
    HS->>PW: postMessage(onBeforeFormSubmit)
    PW->>DL: push hubspot-form-data
    HS->>PW: postMessage(onFormSubmitted)
    PW->>DL: push hubspot-form-success
    DL->>GTM: Trigger fires

GTM Setup Guide

Step 1: Create the Custom HTML Tag

  1. GTM → Tags → New → Custom HTML
  2. Name: cHTML – HubSpot Form Listener
  3. Paste the installation code below
  4. Trigger: All Pages (Pageview)

Step 2: Create Two Custom Event Triggers

Trigger 1, Pre-Submit Data:

  • Event name: hubspot-form-data
  • Use this to capture field data before submission

Trigger 2, Conversion Trigger:

  • Event name: hubspot-form-success
  • Attach GA4, Google Ads, Meta pixel to this trigger

Step 3: Create DataLayer Variables

Variable NameDL KeyPurpose
DLV – HS Form GUIDhs-form-guidUnique identifier of the HubSpot form
DLV – HS Form Datahs-formDataArray of submitted form fields (from pre-submit event)

Installation

html
<!-- GTM Custom HTML Tag: HubSpot Form Listener -->
<!-- Trigger: All Pages (Pageview) -->
<script>
window.addEventListener('message', function(event) {
  if (event.data.type === 'hsFormCallback') {
    
    if (event.data.eventName === 'onBeforeFormSubmit') {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        'event': 'hubspot-form-data',
        'hs-form-guid': event.data.id,
        'hs-formData': event.data.data
      });
    }
    
    if (event.data.eventName === 'onFormSubmitted') {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        'event': 'hubspot-form-success',
        'hs-form-guid': event.data.id
      });
    }
    
  }
});
</script>

Data Layer Output

Pre-Submit Event (Field Data)

json
{
  "event": "hubspot-form-data",
  "hs-form-guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "hs-formData": [
    { "name": "firstname", "value": "Jane" },
    { "name": "email", "value": "jane@company.com" },
    { "name": "company", "value": "Acme Corp" }
  ]
}

Success Event (Conversion)

json
{
  "event": "hubspot-form-success",
  "hs-form-guid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Trigger Configuration

Primary Conversion Trigger

Trigger Type: Custom Event Event Name: hubspot-form-success

Per-Form Trigger (Specific Form GUID)

Trigger Type: Custom Event Event Name: hubspot-form-success Condition: DLV – HS Form GUID | equals | a1b2c3d4-e5f6-7890-abcd-ef1234567890

Variables to Capture

Variable NameDL KeyType
DLV – HS Form GUIDhs-form-guidData Layer Variable
DLV – HS Form Datahs-formDataData Layer Variable

To extract the email from hs-formData, use a Custom JavaScript Variable:

javascript
function() {
  var data = {{DLV - HS Form Data}};
  if (!data) return '';
  for (var i = 0; i < data.length; i++) {
    if (data[i].name === 'email') return data[i].value;
  }
  return '';
}

GA4 Mapping Recommendations

GA4 EventParameterGTM Variable
generate_leadform_idDLV – HS Form GUID
generate_leadmethod"hubspot"
generate_leaduser_emailCustom JS (extract from formData)

Debugging

Common Issues

ProblemCauseFix
No events in dataLayerHubSpot form embedded via API (not standard embed)Check HubSpot embed method
Fires on page loadpostMessage from HubSpot on initAdd if (event.origin.includes('hubspot')) check
hs-formData emptyUsing success event (not pre-submit)Use hubspot-form-data event for field data
Double firesTwo listenersRemove duplicate GTM containers

Origin Filtering (Recommended for Security)

javascript
window.addEventListener('message', function(event) {
  // Only process messages from HubSpot domains
  if (!event.origin.includes('hs-scripts.com') && 
      !event.origin.includes('hubspot.com') && 
      !event.origin.includes('hsforms.net')) return;
  
  if (event.data.type === 'hsFormCallback') {
    // ... rest of listener
  }
});

Best Practices

  1. Always use hubspot-form-success for conversion tracking, never hubspot-form-data (which fires before the submission completes server-side)
  2. Create a GTM Lookup Table mapping form GUIDs to human-readable names for GA4 reporting
  3. Use the GUID, not position, HubSpot form positions change; the GUID is stable
  4. Consent gate the field data push, Only push PII fields (email, name) if the user has consented to marketing cookies
  5. Test with HubSpot's own tracking disabled, Avoid double-counting HubSpot's native analytics + your GTM tracking

Related Listeners

FAQ

Q: My HubSpot form is embedded via the HubSpot CMS, not WordPress. Does this still work? A: Yes, the PostMessage API is the same regardless of CMS.

Q: Does this capture the HubSpot-generated hs_context field? A: The hs-formData array includes all form fields. hs_context is a hidden field with session/browser metadata.

Q: Can I track multi-step HubSpot forms? A: onBeforeFormSubmit fires on each step submission. Use it to track step completions; use onFormSubmitted for the final conversion.

Q: What's the difference between hs-form-guid and the form portal ID? A: The guid is unique per form. The portal ID is your HubSpot account ID. Use the guid for per-form segmentation.