Hi HubSpot Community,
I’m embedding a HubSpot form in my React app using the embed script method like this:
<Script src="https://js-eu1.hsforms.net/forms/embed/26086350.js" strategy="lazyOnload" />
<div
className="hs-form-frame"
data-region="eu1"
data-form-id="21afe108-56eb-402e-85a7-2618461d0198"
data-portal-id="26086350"
/>
I want to track form submissions in React and fire custom logic (e.g., console.log or analytics events) after the user submits the form.
I tried listening for the “HS_CTA_FORM_SUBMITTED” event on window and message events with type “hsFormCallback” and eventName “onFormSubmitted”, but these are inconsistent or don’t fire reliably.
Also, I’m dynamically loading the embed script and checking for the iframe load event to detect when the form is ready.
My questions:
- What is the recommended way to detect HubSpot form submission events when using the embed script method in React?
- Is there an official supported callback or event I can hook into?
- Should I switch to the hbspt.forms.create() API instead for better event handling? If so, how do I migrate my form?
- Are there any best practices for integrating HubSpot forms in React apps to track submissions reliably?
Thanks in advance for any guidance!
Hey there 
Great question—and yes, you’re definitely running into the quirks that come with embedding HubSpot forms in React environments (especially with the newer Next.js script strategies).
Recommended: Use hbspt.forms.create() for Reliable Event Handling
The embed.js method you’re using is static and iframe-based, so it’s difficult to hook into reliably for submission events in React. HubSpot does not officially support a consistent postMessage or global event with the embed script alone—that’s why “hsFormCallback” or “HS_CTA_FORM_SUBMITTED” fire inconsistently (if at all).
Switching to hbspt.forms.create() gives you full control, including access to lifecycle callbacks like onFormSubmit, onFormReady, and onFormSubmitted.
Migration Example: From Embed Script → hbspt.forms.create()
import { useEffect } from ‘react’; const HubspotForm = () => { useEffect(() => { const loadForm = () => { if (window.hbspt) { window.hbspt.forms.create({ region: ‘eu1’, portalId: ‘26086350’, formId: ‘21afe108-56eb-402e-85a7-2618461d0198’, target: ‘#hubspotForm’, onFormSubmitted: () => { console.log(‘
HubSpot form submitted!’); // Add your analytics/event logic here }, }); } }; const script = document.createElement(‘script’); script.src='https://js-eu1.hsforms.net/forms/v2.js’; script.async = true; script.onload = loadForm; document.body.appendChild(script); }, []); return <div id=“hubspotForm” />; }; export default HubspotForm;
Why This Works Better
-
hbspt.forms.create() guarantees access to lifecycle events.
-
It avoids iframe message listeners and inconsistencies across environments like Next.js.
-
You retain full React control: form is injected into a <div> instead of hardcoded <iframe> DOM.
A Few Best Practices for React + HubSpot Forms
- Don’t SSR forms: Always load forms client-side (useEffect) to avoid hydration mismatches.
- Use the onFormSubmitted callback instead of global events or postMessage.
- Load v2.js from the correct region (you’ve got eu1, which is correct for EU-hosted forms).
- If embedding multiple forms, ensure you isolate target elements by unique IDs.
Let me know if this helps, or if you have any other questions!

Was I able to help answer your question? Help the community by marking it as a solution.
Thanks for the detailed answer!
Just to clarify, my main challenge is tracking the form submission in Google Tag Manager while using the embed script (not hbspt.forms.create).
I already have a new form and would prefer not to migrate or recreate it.
Is there any supported way to trigger GTM events with the embed script, or any recommended workaround?
Thanks again!