By default, Contact Form 7 does not save form submissions in your WordPress database. While this helps keep the plugin lightweight, many developers and clients prefer having a record of all submissions for reference, analytics, or backup. Sure, there are plugins like Flamingo that do this—but what if you want a custom solution that’s lean, flexible, and fully under your control?
In this tutorial, we’ll walk through how to save Contact Form 7 submissions directly to your database using a simple WordPress hook. You’ll also learn how to sanitize data properly and set yourself up to build a custom admin interface later if needed.
Step 1: Use the wpcf7_before_send_mail
Hook
Contact Form 7 provides a handy action hook called wpcf7_before_send_mail
, which is triggered after form validation but before the email is actually sent. It’s the perfect place to intercept data and store it in your custom table.
Here’s the code:
add_action( 'wpcf7_before_send_mail', 'save_cf7_submission_to_db' );
function save_cf7_submission_to_db( $contact_form ) {
$submission = WPCF7_Submission::get_instance();
if ( $submission ) {
$data = $submission->get_posted_data();
global $wpdb;
$table_name = $wpdb->prefix . 'cf7_submissions'; // wp_cf7_submissions
$wpdb->insert( $table_name, [
'name' => sanitize_text_field( $data['your-name'] ),
'email' => sanitize_email( $data['your-email'] ),
'message' => sanitize_textarea_field( $data['your-message'] ),
'submitted_at' => current_time( 'mysql' ),
] );
}
}
Step 2: Create the Custom Table (One-Time Setup)
Before storing data, you need to create the custom table wp_cf7_submissions
. You can do this via phpMyAdmin or run the following code once during theme/plugin activation:
function create_cf7_submissions_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'cf7_submissions';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
email varchar(255) NOT NULL,
message text NOT NULL,
submitted_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}
You can hook this into a custom plugin’s register_activation_hook()
or run it once during development.
Why Sanitization Matters
Notice how we use:
sanitize_text_field()
for namessanitize_email()
for email inputsanitize_textarea_field()
for longer messages
Always sanitize data before saving it to the database, even if it passes Contact Form 7 validation. This ensures your site is secure from malicious input and XSS attacks.
What’s Next? Build a Custom Admin Viewer
Now that you’re storing form entries in the database, you can build a custom admin panel to view submissions. This could be a simple WP Admin menu page that lists entries using WP_List_Table
, or even a frontend dashboard with pagination, filtering, and CSV export.
You can also expand the functionality by:
- Adding custom form field support
- Saving submission source URL or user IP
- Sending the data to third-party APIs (e.g., CRM, Google Sheets)
Conclusion
With just a few lines of code, you can store Contact Form 7 submissions in your WordPress database without relying on third-party plugins. It’s clean, efficient, and gives you full control over how your data is handled.
If you’re building advanced forms, lead capture systems, or internal tools for clients, this approach provides a solid foundation you can scale and customize as needed.