Skip to content

Embedded forms & the event relay

When you embed a Recruitly form on your own site in an iframe, analytics fired inside the iframe is unreliable: GA4 sets first-party cookies scoped to the iframe's origin, which makes them third-party from the browser's point of view — Safari and Firefox block them outright and Chrome partitions them.

Recruitly's fix: inside an embed, the form's gtag is wrapped so every command is also posted to the parent window. Your page listens and re-fires the command into its own first-party GA — so form interactions land in your GA4 property with full attribution, alongside the rest of your site traffic.

Recruitly iframe            Your page
gtag('event', ...) ──postMessage──▶ listener ──▶ your gtag.js ──▶ your GA4

The snippet

Add this to any page that embeds a Recruitly form, after your Google Analytics initialisation code (gtag.js):

html
<script>
window.addEventListener('message', function(event) {
  if (event.data && event.data.type === 'recruitly_ga_event' && window.gtag) {
    window.gtag.apply(null, event.data.args);
  }
});
</script>

The same snippet, with a Copy button, lives in Recruitly under Marketplace → Google Analytics → Configuration.

Requirements:

  • Your own GA4 (gtag.js) must be initialised before the listener fires — put the snippet after it.
  • The listener must be on the page that contains the iframe.
  • If you filter messages by event.origin, allow the origin of the Recruitly form you embed.

Message contract

Every message has this shape:

FieldValue
event.data.typeAlways the string 'recruitly_ga_event'
event.data.argsThe raw gtag() argument list, e.g. ['event', 'generate_lead'] or ['config', 'G-XXXXXXXXXX']

Not using gtag? Route it yourself

Because the relay carries the raw argument list, you can feed it into Google Tag Manager or any other collector instead of window.gtag:

html
<script>
window.addEventListener('message', function (event) {
  if (!event.data || event.data.type !== 'recruitly_ga_event') return;
  var args = event.data.args; // e.g. ['event', 'generate_lead', {...}]
  if (args[0] === 'event') {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: 'recruitly_' + args[1],   // → GTM custom event trigger
      recruitlyParams: args[2] || {},
    });
  }
});
</script>

Create a GTM Custom Event trigger for recruitly_generate_lead (etc.) and forward it to GA4 or any tag you like.

Verifying the relay

  1. On the page hosting the iframe, open the browser console and run:

    js
    window.addEventListener('message', e => {
      if (e.data?.type === 'recruitly_ga_event') console.log('relay:', e.data.args);
    });
  2. Interact with the embedded form (submit a test lead) — you should see the relayed commands, including ['event', 'generate_lead'] on success. This proves the iframe side is working.

  3. If commands appear in the console but nothing reaches GA4, your snippet/listener isn't installed after gtag.js — fix the ordering.

  4. Confirm end-to-end in GA4 Admin → DebugView or Reports → Realtime.

Troubleshooting

SymptomCause / fix
Console shows relayed commands, GA4 shows nothingListener missing or registered before gtag.js initialised.
Nothing in the console at allThe form isn't actually in an iframe (the relay only activates in embeds), or the embed failed to load — check the iframe renders.
Events land but with no campaign/source attributionExpected when only iframe-side GA runs. Attribution comes from your page's GA — which is why the relay exists. Install the snippet.
Duplicate page_viewsDon't re-fire relayed config/page_view calls into a page that already tracks its own page views — filter to args[0] === 'event' if you customise the listener (the standard snippet is fine for most setups).

Recruitly — Recruitment CRM for Agencies