Skip to content

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.

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. Check response.errors before 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() form_submission

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
});
});
})();
  1. Create a Custom Event trigger

    • Trigger type: Custom Event
    • Event name: form_submission
    • This fires: Some Custom EventsDLV - form_vendor equals ninja_forms
  2. Create Data Layer Variables

    • DLV - form_vendorform_vendor
    • DLV - form_idform_id
    • DLV - form_titleform_title
  3. 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
  4. Test in Preview mode

    Submit a Ninja Form successfully and confirm the event appears. Then submit with validation errors and confirm no event fires.

Tag Configuration

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

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.

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.

  1. Open GTM Preview mode on a page with a Ninja Form.
  2. Submit with valid data. Confirm form_submission appears with correct form_id and form_title.
  3. Test failure path: submit with a required field empty. Confirm nfFormSubmitResponse fires in the browser console but no form_submission event appears in the dataLayer.
  4. Verify Tags Fired lists the GA4 tag.
  5. In GA4 DebugView, confirm generate_lead arrives within 15 seconds.
  6. 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.

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.