To track form abandonment in Contact Form 7, you need to listen for focusin and focusout events on each field, store partial values in a temporary buffer, and ship that buffer to a backend (or analytics tool) when the visitor leaves the page without firing CF7's wpcf7submit event. CF7 itself ships nothing for this — you add it with a small JavaScript snippet, a companion plugin like CFDB7 or Insiteful, or a session-capture tool that handles it natively.
Why doesn't Contact Form 7 track abandoned submissions by default?
Contact Form 7 is, by design, a form renderer. Its job is to output a <form> element, validate it, and email the result on successful submission. The plugin's database tables only store sent mail metadata — it has no concept of "a visitor who started typing but bailed."
The companion plugin most CF7 users reach for, Flamingo (also by Takayuki Miyoshi), only stores messages that were actually submitted. From the Flamingo plugin page on WordPress.org: it logs inbound messages from Contact Form 7. There is no row created for a partial fill.
That leaves a sizable blind spot. Industry data on form-abandonment-rate consistently shows that the majority of visitors who start a form never finish one. If you only see the finishers, you have no idea what is wrong with the rest.
What Flamingo does and doesn't do
| Capability | Flamingo | What you actually need |
|---|---|---|
| Stores sent submissions | Yes | Yes |
| Stores partial fills | No | Yes |
| Field-level focus events | No | Yes |
| Time-on-field metrics | No | Yes |
| Session context (URL, referrer) | Partial | Yes |
| Replay of the visitor's session | No | Helpful for debugging |
What do you actually want to capture from an abandoned CF7 form?
Before writing any code, decide what "tracking abandonment" means for your team. The useful signals fall into four buckets:
- Field-level engagement: which inputs the visitor focused, in what order.
- Partial values: what they typed before leaving (with PII handled carefully — see the GDPR section).
- Drop-off field: the last field they touched. This is the single highest-leverage data point — it tells you which field is killing your form.
- Session context: page URL, referrer, device, time of day, how far down the page they scrolled.
If you only have time to capture one thing, capture the drop-off field. It is the same metric that any funnel-analysis tool would surface, scoped down to a single form.
Method 1: A no-plugin JavaScript snippet for CF7
The simplest approach is a vanilla JavaScript snippet you drop into your theme's functions.php (or, more cleanly, a custom plugin file). It listens for the three events that matter — focusin, focusout, and CF7's own wpcf7submit — and posts a beacon to your endpoint when the visitor leaves the page.
(function () {
const form = document.querySelector('.wpcf7-form');
if (!form) return;
const session = {
formId: form.getAttribute('data-form-id') || 'cf7',
startedAt: null,
fieldsTouched: [],
lastField: null,
submitted: false,
};
form.addEventListener('focusin', (e) => {
if (!session.startedAt) session.startedAt = Date.now();
const name = e.target.name;
if (!name) return;
if (!session.fieldsTouched.includes(name)) {
session.fieldsTouched.push(name);
}
session.lastField = name;
});
// CF7 fires this on successful submission
document.addEventListener('wpcf7submit', () => {
session.submitted = true;
});
// Send the abandonment beacon when the page is hidden
const flush = () => {
if (session.submitted || !session.startedAt) return;
const payload = JSON.stringify({
...session,
endedAt: Date.now(),
url: location.href,
referrer: document.referrer,
});
navigator.sendBeacon('/wp-json/closetrace/v1/abandon', payload);
};
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') flush();
});
window.addEventListener('pagehide', flush);
})();
A few notes on the snippet:
navigator.sendBeaconis the right API here. Unlikefetch, it is guaranteed to fire even as the page unloads, and browsers explicitly support it for analytics use.- The endpoint
/wp-json/closetrace/v1/abandonis illustrative — you can register a real WordPress REST route withregister_rest_route, or post directly to a third-party collector. - The snippet stores field names, not values. That is the most privacy-safe default. If you do want to capture values, see the GDPR section below.
- CF7's
wpcf7submitevent fires on the document, not the form. Don't change that — it is part of CF7's public JavaScript API.
Wiring the endpoint in WordPress
If you want to keep everything inside WordPress, register a REST route in a small plugin file:
add_action('rest_api_init', function () {
register_rest_route('closetrace/v1', '/abandon', [
'methods' => 'POST',
'callback' => function ($request) {
$payload = $request->get_json_params();
// Persist to a custom table, send to a queue, or relay to your analytics tool.
do_action('closetrace_form_abandoned', $payload);
return new WP_REST_Response(['ok' => true], 200);
},
'permission_callback' => '__return_true',
]);
});
This approach gives you full control and zero vendor lock-in, but you own the storage, the dashboarding, and the reporting. For a single landing page that is fine. For a marketing site with dozens of forms it gets old fast — which is where plugins come in.
What are the best plugin options for CF7 abandonment tracking?
Three CF7-compatible options cover most teams. They sit at very different points on the price-vs-power curve.
Option A: CFDB7 (free, storage only)
CFDB7 saves every Contact Form 7 submission to your WordPress database. It does not capture abandonment by itself, but combined with the snippet above it gives you a single place to query both completed and partial submissions. Good fit: small WordPress sites that already use CF7 and want self-hosted storage.
Option B: Insiteful (paid, opinionated)
Insiteful is a hosted form-analytics tool that drops in via a script tag and works with Contact Form 7 out of the box. It auto-saves partial entries, surfaces field-level drop-off, and lets you trigger recovery emails for abandoners. Good fit: lead-gen teams who care about email recovery and don't want to wire up storage themselves.
Option C: CloseTrace (full session context)
CloseTrace attaches every abandoned CF7 fill to a full session-replay so you can see what the visitor was doing before they bailed — the rage-click on a misaligned label, the dead-click on a non-button div, the validation error they couldn't dismiss. Good fit: teams that already invest in CRO and want field-level metrics plus the qualitative "why."
| Feature | CFDB7 | Insiteful | CloseTrace |
|---|---|---|---|
| Stores completed submissions | Yes | Yes | Yes |
| Captures partial fills | No (requires snippet) | Yes | Yes |
| Field-level drop-off report | No | Yes | Yes |
| Session replay attached | No | No | Yes |
| Heatmaps | No | No | Yes |
| Self-hosted storage | Yes | No | No |
| Pricing | Free | Paid | Paid (free tier) |
What about GDPR and partial form data?
Capturing partial form data is the part of CF7 abandonment tracking most teams get wrong. The moment you store what someone typed — even an email they never submitted — you are processing personal data under GDPR Article 4(1).
A safe default for most marketing sites:
- Capture field names and engagement events, not values.
- If you do capture values, mask anything that looks like an email, phone number, name, or free-text field. Tools that support pii-masking at the SDK level make this much easier.
- Treat the abandonment record the same way you treat any other lead record: include it in your DSAR (data subject access request) workflow and your retention policy.
- Get consent before loading the script if your interpretation of ePrivacy / TTDSG requires it.
For the full breakdown, see our companion post on whether session replay is GDPR compliant — the same principles apply to partial form capture.
Frequently asked questions
Does Contact Form 7 store any data on abandoned submissions?
No. Out of the box, Contact Form 7 does not store any submission data at all — sent or unsent. The companion plugin Flamingo only stores submissions that were successfully sent. To capture partial fills you need either a custom JavaScript snippet, a third-party form analytics plugin, or a session capture tool.
Will tracking partial CF7 submissions slow down my WordPress site?
A correctly written abandonment snippet adds well under 5 KB of JavaScript and uses navigator.sendBeacon, which runs asynchronously and never blocks the page. The performance impact is negligible compared to the average WordPress theme. Hosted tools like Insiteful or CloseTrace typically load a single async script tag in the 30-80 KB range.
Can I trigger a recovery email when someone abandons a CF7 form?
Yes, but only if you captured an email address before they left. Practically, that means firing your beacon on focusout of the email field, not just on page-hide — so by the time the visitor blurs the email and starts on the next field, you already have it. Then your backend can decide whether to enqueue a recovery email based on your consent rules.
Is form abandonment tracking GDPR compliant?
It can be, with the same conditions that apply to any analytics: a lawful basis (consent or legitimate interest), data minimization, a documented retention policy, and DSAR support. Capturing only field names and timing is generally low-risk; capturing partial values requires more care. See is session replay GDPR compliant for the long form.
What is the difference between form abandonment and bounce rate?
Bounce rate vs exit rate measures whether a visitor left your site after viewing a single page. Form abandonment measures whether a visitor started filling a specific form and did not submit it. A visitor can have a low bounce rate (they explored your site) and still abandon every form they touched. The two metrics answer different questions.
Does CF7 fire any JavaScript event for abandoned forms?
No. CF7 fires wpcf7submit, wpcf7mailsent, wpcf7mailfailed, wpcf7invalid, and wpcf7spam — all on successful or attempted submission. There is no built-in "abandoned" event. You have to detect abandonment yourself by tracking focus events and listening for visibilitychange or pagehide.
Where to go next
If you want the no-plugin route, the snippet above is enough to get started — register the REST endpoint, drop the JS in your theme, and watch the data come in. If you want field-level analytics, replays, and funnel-drop-off-rate without writing storage code, CloseTrace gives you all of it in a single script tag and works with Contact Form 7 out of the box.
For more on the broader problem this solves, read form abandonment: the quiet revenue killer.