Track Ninja Forms Submissions
Ninja Forms is AJAX-only and built on Backbone/Marionette. Submissions fire a jQuery event that includes the full server response — including validation errors. This recipe wires the right event, filters out errors, and sends a GA4 generate_lead.
Valid as of April 2026, Ninja Forms 3.8.x.
The success signal
Section titled “The success signal”Ninja Forms is entirely AJAX — there is no non-AJAX mode. The relevant jQuery events are:
nfFormReady— fires when a form’s JS is initialised. Useful for marking forms as ready.nfFormSubmitResponse— fires after every submit attempt, with the full server response. Checkresponse.errorsbefore treating as success.
The response payload shape:
{ data: { form_id: 3, settings: { title: 'Contact', ... }, fields: { ... }, actions: { ... }, extra: { ... } }, errors: { fields: {}, form: [] } // empty arrays/objects = success}A submission succeeded if response.errors.form is an empty array and response.errors.fields is an empty object.
dataLayer push pattern
Section titled “dataLayer push pattern”Only pushes when response.errors is empty — never on validation failures.
(function () { if (!window.jQuery) return;
window.jQuery(document).on('nfFormSubmitResponse', function (event, response) { var errors = response && response.errors; var hasFieldErrors = errors && errors.fields && Object.keys(errors.fields).length > 0; var hasFormErrors = errors && Array.isArray(errors.form) && errors.form.length > 0;
if (hasFieldErrors || hasFormErrors) { return; // validation failed — do not track as success }
var data = response && response.data; if (!data) return;
window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'form_submission', form_vendor: 'ninja_forms', form_id: String(data.form_id), form_title: (data.settings && data.settings.title) || 'ninja_' + data.form_id }); });})();GTM setup
Section titled “GTM setup”-
Create a Custom Event trigger
- Trigger type: Custom Event
- Event name:
form_submission - This fires: Some Custom Events →
DLV - form_vendorequalsninja_forms
-
Create Data Layer Variables
DLV - form_vendor→form_vendorDLV - form_id→form_idDLV - form_title→form_title
-
Create a GA4 Event Tag
- Event name:
generate_lead - Parameters:
form_vendor→{{DLV - form_vendor}}form_id→{{DLV - form_id}}form_title→{{DLV - form_title}}
- Trigger: the Custom Event trigger above
- Event name:
-
Test in Preview mode
Submit a Ninja Form successfully and confirm the event appears. Then submit with validation errors and confirm no event fires.
GA4 - generate_lead (Ninja Forms)
- Type
- Google Analytics: GA4 Event
- Trigger
- Custom Event - form_submission (ninja_forms)
- Variables
-
DLV - form_vendorDLV - form_idDLV - form_title
Alternative: Marionette Radio channel
Section titled “Alternative: Marionette Radio channel”Ninja Forms exposes a Marionette Radio channel forms that you can subscribe to for a cleaner integration — this bypasses jQuery entirely:
(function () { function subscribe() { if (!window.Marionette || !window.nfRadio) { return setTimeout(subscribe, 200); } var channel = window.nfRadio.channel('forms'); channel.reply('submit:response', function (response) { var errors = response && response.errors; if (errors && (errors.form.length || Object.keys(errors.fields).length)) return; // same dataLayer.push as above }); } subscribe();})();Use this only if you have a reason to avoid jQuery. For most sites, the jQuery event is simpler and equivalent.
Capturing field values
Section titled “Capturing field values”Field values are in response.data.fields, keyed by field ID:
var fields = response.data.fields;// fields[12].value, fields[13].value, etc.Field IDs are stable per form but not per site — field 12 on form A is not field 12 on form B. Use the Ninja Forms field key (a human-readable slug you set in the field settings) via fields[id].key if you need portability.
Test it
Section titled “Test it”- Open GTM Preview mode on a page with a Ninja Form.
- Submit with valid data. Confirm
form_submissionappears with correctform_idandform_title. - Test failure path: submit with a required field empty. Confirm
nfFormSubmitResponsefires in the browser console but noform_submissionevent appears in the dataLayer. - Verify Tags Fired lists the GA4 tag.
- In GA4 DebugView, confirm
generate_leadarrives within 15 seconds. - Test conditional logic: if your form uses Ninja Forms’ conditional logic to show/hide required fields, submit with a hidden-but-required field to confirm the error filter still works.
Common gotchas
Section titled “Common gotchas”Pushing on every nfFormSubmitResponse. The event fires on failures too. Always check response.errors before pushing.
Conditional logic re-renders. Ninja Forms re-renders fields when conditional logic triggers. The delegated jQuery(document).on(...) listener survives this; binding directly to .nf-form-cont does not.
No non-AJAX fallback. Unlike Gravity Forms or WPForms, Ninja Forms has no non-AJAX mode. If the AJAX endpoint is blocked (ad blocker, firewall), submissions fail silently — you will see no event, but the user will see a stuck spinner.
Multi-part forms. Ninja Forms’ multi-part add-on fires nfFormSubmitResponse only on final submission. If you want step tracking, listen to nfChangeSlide (fires on each step change) — the event passes the slide index.
Form in a modal. Modal plugins often load the form inside an iframe or re-initialise Ninja Forms’ JS on modal open. The delegated listener on document handles in-page modals; for iframes, postMessage is required (out of scope here).
Multiple forms on one page. response.data.form_id disambiguates automatically. No per-form binding needed.