Client events — GTM, GA4, and Meta
Form submissions are conversion events. Formie lets you configure analytics payloads in the form builder — no custom JavaScript required for the common GTM, GA4, and Meta setups. This walkthrough wires a contact form through Google Tag Manager, with notes for headless apps and multi-page forms.
Prerequisites
- A form with at least one page (single-page is fine for a first pass)
- Google Tag Manager installed on the site (
window.dataLayeravailable) - Tracking and Analytics
How client events work
- You configure events per page in the form builder Tracking tab.
- After a successful page submit, Formie resolves payload values server-side (field values, form metadata, and so on).
- Formie pushes each event to
window.dataLayerand dispatches aformie:client-eventDOM event.
For Ajax and headless submissions, the same resolved events appear in the submit response as clientEvents.
Each event has:
- Event Name — the analytics identifier (GTM uses this as the
eventproperty). - Payload properties — key/value pairs pushed to
dataLayer.
Use the variable picker for values. Field tokens use stable references like {field:a1b2c3}, not raw handles.
Step 1 — Enable client events on the form
- Open your contact form in the form builder.
- Select the page you want to track (for a single-page form, that is the only page).
- Open the Tracking tab.
- Enable Client Events.
Step 2 — Add a GTM page submit event
Click Add event and choose the GTM page submit template (or add a blank event and configure manually).
Example payload:
| Property | Value (from variable picker) |
|---|---|
formHandle | {form:handle} |
formTitle | {form:title} |
email | {field:…} (pick your Email field) |
That produces a dataLayer.push() equivalent to:
window.dataLayer.push({
event: 'formPageSubmission',
formHandle: 'contactForm',
formTitle: 'Contact us',
email: '[email protected]',
});Form-wide defaults
For events that should fire on every page, define them once under Behaviour → Client Event Defaults, then use Apply defaults to all pages. Pages with client events enabled but no page-specific events inherit the defaults.
Step 3 — Configure GTM
In Google Tag Manager:
- Create a Custom Event trigger named
Form Page Submissionwith event nameformPageSubmission(or whatever event name you configured). - Create Data Layer Variables for each payload key (
formHandle,email, and so on). - Create a Google Analytics: GA4 Event tag (or your preferred tag type) bound to that trigger.
- Map Data Layer variables to tag parameters.
Publish the container and test with GTM Preview mode.
Step 4 — GA4 generate_lead
Formie includes a GA4 generate_lead template. Add it from Add event on the final page of a multi-page form (or the only page of a single-page form).
The template prompts you to map:
- Email field
- Phone field (optional)
- Value/currency fields (optional, for lead value)
After insertion, the event is stored like any other client event and can be edited freely. In GTM, create a trigger for the configured event name (often generate_lead) and a GA4 tag with event_name set to generate_lead and user properties mapped from Data Layer variables.
Step 5 — Meta Lead event
Use the Meta Lead template for Facebook/Meta Pixel setups. Map the email field when prompted.
In GTM (or your Meta pixel integration), bind a tag to the Formie event name and pass hashed user data according to Meta's current requirements. Formie resolves the email server-side before the event fires — the front end receives the resolved value in the payload, not a template token.
Multi-page forms
On multi-page forms, client events fire after each successful page submit, not only on the final page.
| Goal | Configure on |
|---|---|
| Track every step | Each page's Tracking tab |
| Track only final conversion | Last page only; use pageContexts when registering custom templates |
| Avoid false conversions in custom JS | Check !result.nextPage before pushing a final event |
If you add custom JavaScript instead of builder events, the result.nextPage check matters for Ajax forms:
formEl?.addEventListener('formie:submit:result', (event) => {
const result = event.detail;
if (!result.ok || result.nextPage) {
return;
}
window.dataLayer.push({ event: 'formSubmission', formHandle: 'contactForm' });
});Conditions
Each client event supports optional conditions — limit when an event fires based on field values, user state, or other rules. Use conditions when one page should push different events depending on a dropdown answer, for example.
Headless and React/Vue apps
Builder-configured events are resolved server-side and returned in the submit response:
{
"success": true,
"clientEvents": [
{
"event": "formPageSubmission",
"payload": {
"event": "formPageSubmission",
"formHandle": "contactForm",
"email": "[email protected]"
}
}
]
}Push them in your framework callback:
<FormieForm
formHandle="contactForm"
transport="rest"
endpoint="https://your-craft-site.test"
onSubmitSuccess={(result) => {
result.clientEvents?.forEach(({ payload }) => {
window.dataLayer?.push(payload);
});
}}
/>For server-rendered Twig forms, Formie pushes to dataLayer automatically — you do not need the callback unless you want additional logic.
Listen for builder events in custom JS
formEl?.addEventListener('formie:client-event', (event) => {
console.log('Client event dispatched:', event.detail);
});Useful when GTM is not on the page, or when you need to fan out to multiple analytics tools.
Redirect and thank-you pages
If the form redirects to a thank-you page after success, put analytics code on the thank-you page instead — the redirect itself is the completion signal. Builder client events still fire before redirect on Ajax forms; for page-reload submits, use the flash-based pattern in Tracking and Analytics.