Skip to main content
Contributor II
February 23, 2021
Solved

Implement Embed Klaviyo forms in SPA apps like GatsbyJS / ReactJS.

  • February 23, 2021
  • 25 replies
  • 7451 views

Hi there, 

I am embedding klaviyo forms in our GatsbyJs project, you can also say ReactJS, as it doesn’t actually refresh the page while navigating between pages, the forms are not getting initialized, as the JS doesn’t go to find for embed code and implement because I believe that the forms are only initialized on DOMContentLoaded event and I didn’t find any trigger to pass formId/ListId and companyId which initializes the form. So, do I have any option to trigger the initialization or I have to do this weird thing called refresh/reload page.

Best answer by cbarley

Hey @karan.tipl, This is a great question. I have also made some React apps and hoped to use Klaviyo embed forms (nothing with Gatsby, but the same general ideas apply).

The short answer is that our forms don’t play nicely with Single Page Applications that use browser-side routing.

The long answer is that after testing any way to get this to work (creating a custom hook to call the Klaviyo.js tracking code, or the forms code that we add to a page with a Klaviyo form on it), it isn’t possible to programmatically refresh the form at this time. The product team is aware of this issue, though, I just don’t have a timeframe available for you on a fix for that.

If you’re using something like react-router, since we don’t re-fire the klaviyo.js script on those page changes, and you can’t programmatically force that without doing a hard-refresh, a Klaviyo embed form might not be the best solution for you.

We do have the ability to redirect existing sign up forms to Klaviyo from the frontend, so in your case, using a custom form and passing those submissions to Klaviyo is your best bet. We also have a server-side version of our Lists API, so if you had the ability to pass the data to your server, then call our Lists V2 API to subscribe somebody to that list, that would also work!

 

25 replies

Contributor II
February 4, 2024

Also pinging in that we have this issue. More and more stores are going PWA. But it seems like this is not a priority at Klaviyo at all, since it has been pointed out for years. 

Contributor I
June 5, 2025

Hi,
I had the same difficulties to integrate a Klaviyo form in my NextJs app.

It turns out the main Klaviyo object can be used to trigger the rendering of the form.

You can shove that inside a UseEffect and move on.

 

const ComponentA: NextPage = () => {
useEffect(() => {
window._klOnsite = window._klOnsite || [];
window._klOnsite.push(['openForm', 'FORM_ID']);
}, []);
return (
<>
<main>
<div className="klaviyo-form-VD6mYY"></div>
</main>
</>
);
};

 

Contributor I
July 10, 2025

@monlouisj did that actually work for you? we tried this and it didn’t do anything, in Klaviyo’s docs it says that push function doesn’t work with embedded forms.

Contributor I
July 23, 2025

Hi ​@engine ,
yes I confirm

Contributor I
May 27, 2026

Posting a less invasive alternative to the iframe workaround in case it's useful.

The fundamental issue is that Klaviyo's onsite script scans the DOM once on initial load and 
doesn't re-scan when an SPA mounts a new form div on navigation. But their internal render is 
re-triggered on pathname changes — driven by a window-level CustomEvent named 
onsite-event-publish, with payload {type: "PAGE_CHANGE", payload: {currentPageUrl: "..."}}. 
Klaviyo's listener fires their render flow whenever the pathname in currentPageUrl differs from
the one they've stored.

The fix: dispatch this event yourself with a unique fake URL whenever the form has come up 
empty. Klaviyo runs its render path, this time finding the current form div. Core dispatch:

window.dispatchEvent(
new CustomEvent("onsite-event-publish", {
detail: {
type: "PAGE_CHANGE",
payload: {
currentPageUrl: `${location.origin}/__klaviyo_retry_${attemptNumber}`
}
}
})
);

Packaged as a drop-in React component (~50 lines, no deps beyond React):

https://github.com/emilrais/klaviyo-spa-fix

<KlaviyoForm id="YOUR_FORM_ID">
<h4>Subscribe!</h4>
</KlaviyoForm>

The component renders <div className="klaviyo-form-<id>" /> (the standard Klaviyo target) and a 
MutationObserver watches its content. The children prop renders only when the form has actually 
populated, so failed renders gracefully degrade to nothing instead of leaving orphaned chrome on
the page. Up to 4 retries handle the (rare) cases where the first synthetic PAGE_CHANGE doesn't
land either.