Contact Form 7 GTM Event Listener
Track Contact Form 7 (CF7) form submissions in Google Tag Manager using the wpcf7mailsent event. Fire GA4 events, Google Ads conversions, and Meta pixels on successful submissions.
Event fired
cf7submissionContact Form 7
Overview
Contact Form 7 (CF7) is the most widely-used WordPress form plugin, powering millions of contact forms worldwide. This listener hooks into CF7's native wpcf7mailsent DOM event to push structured data into the GTM dataLayer the moment a form submission succeeds, letting you fire any analytics or advertising tag without modifying theme files.
Event fired: cf7submission
Trigger type: Custom Event
Credit: Julius Fedorovicius (Analytics Mania)
Why Use This Listener
| Without Listener | With Listener |
|---|---|
| No way to track form submissions in GTM natively | Full conversion tracking in GA4, Google Ads, Meta |
| Must use GTM's Form Submit trigger (unreliable) | Event fires only on confirmed email send |
| No form ID data available | formId and full field response data captured |
| Can't distinguish between multiple forms | Each form submission identified by unique formId |
CF7 does not natively push to the dataLayer. The GTM built-in Form Submit trigger fires on button click, before validation. This listener fires after the email has been sent, making it the accurate conversion signal.
Common Use Cases
- Lead generation tracking, Know exactly when a prospect submits a contact request
- Multi-form conversion segmentation, Track which of your 5 different contact forms converts best
- Google Ads conversion import, Send confirmed form submissions as conversion actions
- Meta (Facebook) Pixel lead events, Fire
Leadstandard events to power retargeting audiences - GA4 funnel analysis, Include form submission as a milestone in your conversion funnel
- Form abandonment analysis, Combine with scroll/time triggers to identify drop-off
How It Works
CF7 dispatches the DOM event wpcf7mailsent on the document when the server confirms the email was sent. The listener uses addEventListener to catch this event, extract the form ID and field values from event.detail, and push them to the dataLayer.
sequenceDiagram
participant U as User
participant CF7 as Contact Form 7
participant JS as Event Listener
participant DL as dataLayer
participant GTM as GTM
participant GA4 as GA4
U->>CF7: Clicks Submit
CF7->>CF7: Validates + Sends Email
CF7->>JS: Dispatches wpcf7mailsent
JS->>DL: dataLayer.push({event: "cf7submission"})
DL->>GTM: Trigger fires
GTM->>GA4: Sends form_submit eventGTM Setup Guide
Step 1: Create the Custom HTML Tag
- Open Google Tag Manager → Tags → New
- Tag type: Custom HTML
- Paste the code below
- Trigger: All Pages (or DOM Ready, see note below)
Step 2: Create the Custom Event Trigger
- Go to Triggers → New
- Trigger type: Custom Event
- Event name:
cf7submission - This trigger = fires only on CF7 successful submissions
Step 3: Create DataLayer Variables
Create two Data Layer Variables in GTM:
| Variable Name | Data Layer Variable Name | Description |
|---|---|---|
| DLV – CF7 Form ID | formId | The unique CF7 form ID |
| DLV – CF7 Response | response | Array of all form field values |
Step 4: Attach Your Tags
Connect your GA4, Google Ads, or Meta tags to the cf7submission trigger.
Installation
<!-- GTM Custom HTML Tag: CF7 Event Listener -->
<!-- Trigger: All Pages (Pageview) -->
<script>
document.addEventListener('wpcf7mailsent', function(event) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'cf7submission',
'formId': event.detail.contactFormId,
'response': event.detail.inputs
});
});
</script>Note on trigger timing: If CF7 script loads asynchronously, fire this listener on Window Loaded instead of DOM Ready to ensure the CF7 form object is available.
Example Implementation
Basic: Form Submit Tracking
document.addEventListener('wpcf7mailsent', function(event) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'cf7submission',
'formId': event.detail.contactFormId,
'response': event.detail.inputs
});
});Advanced: Extract Specific Fields
document.addEventListener('wpcf7mailsent', function(event) {
// Extract individual field values
var inputs = event.detail.inputs;
var email = '';
var name = '';
inputs.forEach(function(input) {
if (input.name === 'your-email') email = input.value;
if (input.name === 'your-name') name = input.value;
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'cf7submission',
'formId': event.detail.contactFormId,
'response': inputs,
'userEmail': email,
'userName': name
});
});Data Layer Output
Successful Submission
{
"event": "cf7submission",
"formId": "1234",
"response": [
{ "name": "your-name", "value": "John Smith" },
{ "name": "your-email", "value": "john@example.com" },
{ "name": "your-message", "value": "Hello, I'd like to enquire about..." }
]
}Trigger Configuration
Single Trigger (All CF7 Forms)
Trigger Type: Custom Event
Event Name: cf7submission
Fires On: All Custom Events
Per-Form Trigger (Specific Form Only)
Trigger Type: Custom Event
Event Name: cf7submission
Condition: DLV - CF7 Form ID equals 1234
Fires On: Some Custom Events
Variables to Capture
| GTM Variable Name | DL Key | Type | Example Value |
|---|---|---|---|
| DLV – CF7 Form ID | formId | Data Layer Variable | "1234" |
| DLV – CF7 Response | response | Data Layer Variable | Array of inputs |
To access individual fields from the response array, use a Custom JavaScript Variable:
function() {
var response = {{DLV - CF7 Response}};
if (!response) return '';
for (var i = 0; i < response.length; i++) {
if (response[i].name === 'your-email') return response[i].value;
}
return '';
}GA4 Mapping Recommendations
| GA4 Event | Parameter | GTM Variable |
|---|---|---|
generate_lead | form_id | DLV – CF7 Form ID |
generate_lead | form_name | Hard-coded or look-up table |
generate_lead | method | "contact_form_7" |
generate_lead | user_email | Custom JS Variable (extract from response) |
GA4 Configuration Example:
Event Name: generate_lead
Parameters:
- form_id → {{DLV - CF7 Form ID}}
- form_type → "contact_form_7"
- method → "web_form"
Debugging
Check in GTM Preview Mode
- Open GTM Preview mode
- Submit a CF7 form on your site
- In the preview panel, look for a
cf7submissionevent in the Events tab - Click it and verify
formIdandresponseare populated
Common Issues
| Problem | Likely Cause | Fix |
|---|---|---|
| Event not firing | CF7 script not loaded when listener runs | Change listener trigger to Window Loaded |
formId is undefined | Wrong DLV key name | Check exact spelling: formId (capital I) |
| Fires on failed submissions | Using wrong approach | wpcf7mailsent only fires on successful sends, no fix needed |
| Multiple fires per submission | Duplicate listeners | Check for duplicate GTM containers or conflicting scripts |
Browser Console Test
Open DevTools → Console and paste:
document.dispatchEvent(new CustomEvent('wpcf7mailsent', {
detail: { contactFormId: '999', inputs: [{name:'test', value:'hello'}] }
}));You should see the event appear in the GTM preview dataLayer panel.
Best Practices
- Use form-specific triggers, If you have different forms for different lead types (contact, demo, newsletter), create separate triggers using the
formIdcondition to fire different GA4 events or different conversion labels - Hash PII before sending, If capturing email/name in the dataLayer for enhanced conversions, use SHA-256 hashing:
crypto.subtle.digest('SHA-256', ...) - Set CF7 "on sent OK" class, In CF7 settings, add a
sentresponse class so you can also style confirmation visually - Combine with scroll depth, Track how far users scroll before contacting you for UX insights
- Test with spam filters, CF7's Flamingo or Akismet might block some submissions; test across form validation scenarios
Performance Considerations
- Impact: Negligible, the listener attaches a single DOM event listener at page load
- Event volume: One event per successful form submission
- No polling, Purely event-driven, zero CPU overhead between submissions
Related Listeners
- Gravity Form, jQuery-based WordPress form tracking
- HubSpot Form, PostMessage-based HubSpot form tracking
- Ninja Form, Ninja Forms submission tracking
- Elementor Form, Elementor page builder form tracking
- Formidable Form, Formidable Forms tracking
- Forminator Form, WPMU Dev Forminator tracking
Example Business Scenarios
Scenario 1: B2B SaaS Lead Qualification
A SaaS company has 3 CF7 forms: "Request Demo" (ID: 100), "Contact Sales" (ID: 101), "General Inquiry" (ID: 102). They create 3 separate conversion actions in Google Ads, each mapped to the respective formId. This lets them see which form generates the highest-quality demos.
Scenario 2: Law Firm Conversion Tracking
A law firm tracks all CF7 submissions as generate_lead events in GA4. They then build an audience of people who submitted the contact form and target them with Google Display retargeting showing client testimonials.
Scenario 3: Agency Multi-Client Reporting
A digital agency manages 20 client WordPress sites, all using CF7. They deploy the same GTM container across all sites. The formId variable lets them filter by form in GA4 explorations to report conversions per client.
FAQ
Q: Does this work with CF7 AJAX submissions?
A: Yes. wpcf7mailsent is dispatched after the AJAX call completes and the email is sent.
Q: What if I use CF7 with reCAPTCHA? A: The listener fires after reCAPTCHA validation passes and the email sends, so only genuine submissions are tracked.
Q: Can I track form starts (not just completions)?
A: Yes, use GTM's built-in Element Visibility trigger on the CF7 form container, or listen to the wpcf7invalid event to capture validation failures.
Q: Does this work with multisite WordPress?
A: Yes, each form has a unique contactFormId. Deploy via GTM and it works across all multisite installations.
Q: How do I get the form name (not just ID)? A: CF7 doesn't expose the form name in the event, use a GTM Lookup Table variable that maps form IDs to human-readable names.
Q: What happens if the email fails to send?
A: wpcf7mailsent only fires on successful sends. wpcf7invalid fires on validation errors. wpcf7spam fires when spam is detected.