What’s a modern contact form plugin for WordPress to replace Contact Form 7?

Fluent Forms is where most WordPress operators who leave Contact Form 7 land, and the migration is uneventful. The decision is really about editor UX, not about whether notifications arrive: every mainstream form plugin hands its mail to WordPress, and the mailer the site already runs is what actually sends it. If mail is already landing on the current site, swapping form plugins is a UX change, not a deliverability fix.

The decision is upstream of the form plugin

Every notification email from a mainstream WordPress contact form passes through core’s wp_mail() function. That is true of Contact Form 7, Fluent Forms, WPForms Lite and Pro, Forminator, Gravity Forms, and Ninja Forms. Whichever mailer plugin the site runs (WP Mail SMTP, FluentSMTP, Post SMTP, or a hand-rolled phpmailer_init filter) replaces or wraps wp_mail() at the pluggable-function layer. The form plugin above it does not know or care. The deliverability axis is settled below the form plugin, not inside it.

One plugin ships an escape hatch. Gravity Forms sells a first-party SendGrid add-on that selects SendGrid as the Email Service for a given notification and sends through SendGrid’s API directly, bypassing wp_mail() for that notification only. The site mailer still handles everything else. Every other mainstream alternative is pure wp_mail() passthrough.

The practical consequence: on a WordPress site whose email is already going out and arriving, the form-plugin decision is about editor UX, licensing, and feature set. The notification-mail step is settled before the form plugin is even installed.

How each form plugin sends its notifications

Plugin Mail transport Filter for mail body or headers Migration surprise
Fluent Forms wp_mail() passthrough fluentform/email_body, fluentform/email_subject, fluentform/email_to for mail content; fluentform/submission_inserted is the post-storage action Importer rebuilds CF7 fields under Fluent Forms’ own field-key schema; downstream code reading CF7 field slugs needs updating
WPForms Lite wp_mail() passthrough wpforms_entry_email_atts (single filter covering subject, message, headers in one $email array, with $fields, $entry, $form_data, and $notification_id alongside it) Lite stores no entries locally; the Flamingo-style log a CF7 operator relied on disappears unless WPForms Pro is bought
Forminator wp_mail() passthrough forminator_custom_form_mail_data for the full mail-data array; forminator_custom_form_mail_admin_message for the admin notification body Conditional-logic rules do not import from CF7’s Conditional Fields add-on; they are rebuilt in Forminator’s own visibility system
Gravity Forms wp_mail() passthrough; first-party SendGrid add-on short-circuits it per-notification gform_notification to modify the notification object before send; gform_pre_send_email for recipient and display-name tweaks License is per-site and paid-only; a free CF7 install becomes a $59-per-year install at the entry (Basic) tier, as of 2026-10
Ninja Forms wp_mail() passthrough; the vendor’s own SMTP add-on is sold separately ninja_forms_run_action_settings to override any email-action setting at submission time (From Address included); ninja_forms_action_email_attachments for attachments Email actions are per-form; a CF7 Mail 2 slot that fanned to a second recipient has no direct importer path and is rebuilt as a second email action
Contact Form 7 wp_mail() passthrough wpcf7_mail_components (full control over recipient, sender, subject, body, additional headers, and attachments in a single hook) Not applicable; this is the plugin being migrated from

A row-by-row read: every mainstream form plugin’s mail transport is wp_mail(), which means the mailer plugin a site already runs survives the migration intact. The filter column matters when existing site code modifies mail headers or body; those filters are plugin-specific and the names do not translate. The migration-surprise column is the one that tends to catch operators off-guard; the next section is why.

What a form-plugin migration actually costs

The shortcode-and-importer story is the easy part. Every mainstream alternative ships an importer that reads Contact Form 7’s [text your-name] shortcode syntax and reconstructs the fields on the other side. The failure modes are downstream of the import.

Mail-filter code moves. An mu-plugin that filters wpcf7_mail_components to rewrite the From: address, add a BCC header, or override Reply-To has no direct equivalent on the new plugin. Each alternative exposes its own hook, and the signatures are different enough that porting is a rewrite, not a search-and-replace. For the common case of forcing a stable From: address so DKIM aligns, see set contact form sender email as the Reply-To for the pattern on each plugin.

Form IDs change. CF7’s form IDs look like wpcf7-f123-p45-o1; the new plugin’s form IDs are its own. Any CRM webhook, Zapier scenario, Google Analytics event, or custom report keyed on the CF7 form ID breaks on the first submission after cutover. The fix is not in the importer; it is in whatever consumes the submission data downstream.

Spam-gate stacks come apart. Flamingo logs CF7 submissions and runs Akismet on them; it is a CF7 companion from the same author, and it does not know how to listen to anything else. Honeypot and hCaptcha integrations are configured per form plugin. The reCAPTCHA keys carry over, but the attachment points do not. Every spam gate the site has in place needs to be reconfigured on the new plugin from the admin interface.

Submission storage parity is not a given. Contact Form 7 stores nothing in the database by itself; Flamingo covers that. Fluent Forms, Forminator, Gravity Forms, and WPForms Pro all store submissions locally. WPForms Lite, notably, stores no entries locally and prompts for a paid-tier upgrade to view them at all. A CF7 operator who had Flamingo running loses their entry log on cutover unless the replacement is one of the plugins that stores locally.

Conditional and multi-recipient routing is uneven. CF7 supports a second mail (Mail 2) with its own template, triggered on every submission or filtered by the Conditional Fields for Contact Form 7 add-on. “The rest” the importer leaves behind is usually a recipient routed by a form-field value (a “which department are you writing to?” dropdown that fans out to three different inboxes) and a second template that quotes different fields in the body. The importers recreate the first notification and leave both of those to the operator.

Together, these are the reason “I will just install the importer and switch” turns into a longer afternoon than expected.

When Contact Form 7 is still the right pick

The reader of this question is often not describing a broken plugin. Contact Form 7 is maintained, has no upsell UI (there is no Pro tier to upsell), and the editor-UX complaint is subjective. The plain-textarea markup is either familiar or archaic depending on the operator. On a site where CF7 is paired with a working mailer, the mail lands because of the mailer, and swapping form plugins changes the editor without changing anything downstream of the submit button.

The honest version of the recommendation: swap off Contact Form 7 for the editor and the feature set, not for the email. The mail step is upstream, and the upstream choice is the one that controls delivery.

Where to go next

If the mail is not landing in the first place, the fix is a mailer plugin, not a form plugin, and the WordPress SMTP plugin shortlist is the starting point. If the From: address is the real problem (DKIM alignment, mail landing in spam), set the contact form sender email as the Reply-To covers the pattern that works across every plugin in the table above. For readers who have not yet installed a form at all, how to create a contact form on WordPress walks through the shorter path.