Skip to content

Everything for WordPress, web development — and beyond

🐛 Contact Form 7 in Elementor popup: why the form reloads the page and how to fix it

🐛 Contact Form 7 in Elementor popup: why the form reloads the page and how to fix it

You add a contact form via Contact Form 7 inside an Elementor Pro popup. The user fills in the fields, clicks "Submit," and the entire page reloads.

No error messages. No submission confirmation. Just a reload and a lost lead.

This is a known conflict: CF7 relies on AJAX submission, but inside a dynamically loaded popup its JavaScript doesn't have time to bind to the form. As a result, the browser performs a standard HTML submit, the very one that causes the reload.

The issue has existed for years, yet it's fixed with just two lines of code. Below are two working solutions: a modern one (cleaner and more reliable) and an alternative from GitHub, plus optional enhancements and a debugging checklist.

💡 Quick overview:

  • Root cause: CF7 initializes on page load, but the popup with the form appears later, so the script knows nothing about its contents
  • Solution 1: reinitialize CF7 when the elementor/popup/show event fires; JavaScript waits for the popup to open and picks up the form
  • Solution 2: track clicks on the button that opens the popup with a delay for the animation; this method comes from a GitHub discussion on Elementor
  • Optional: reset the form on reopen and auto-close the popup after a successful submission
  • Debugging checklist: browser console, jQuery conflicts, caching plugins

Why CF7 breaks specifically in a popup

Programmer fixing a Contact Form 7 bug in WordPress

Contact Form 7 is built on AJAX: the form submits without reloading, field validation happens on the fly, and error or success messages appear instantly. However, this entire mechanism binds to the DOM on page load via a call to wpcf7.init().

Elementor Pro loads popup content dynamically, after the DOMContentLoaded event. When a user clicks a button and the popup opens, its HTML is inserted into the document, but CF7 knows nothing about it. The form inside the popup remains uninitialized.

What happens next: without an active AJAX handler, the browser performs a standard HTML form submission. The action attribute fires, and the page reloads. The popup closes naturally (its state resets on navigation). The user sees a reload and leaves.

Many developers try to treat the symptoms: they block the popup from closing via event.stopPropagation(), override Elementor's internal functions, or prevent form submission with e.preventDefault(). None of these methods address the root cause (the missing CF7 initialization). Some even break popup animations or Elementor itself across the entire site.

There is only one reliable solution: wait for the popup to open and explicitly call wpcf7.init() for each form inside.

Solution 1: reinitialization on popup open (modern approach)

This method uses Elementor's native elementor/popup/show event. Place the code in your active theme's functions.php or add it via a snippets plugin like Code Snippets.

Create a backup of functions.php before editing.

1/**
2 * Reinitialize Contact Form 7 when opening an Elementor popup.
3 * Fixes page reload after form submission.
4 */
5function sdstudio_cf7_reinit_in_popup() {
6 ?>
7 <script>
8 jQuery( document ).on( 'elementor/popup/show', function() {
9 document.querySelectorAll( '.wpcf7 form' ).forEach( function( form ) {
10 if ( typeof wpcf7 !== 'undefined' ) {
11 wpcf7.init( form );
12 }
13 });
14 });
15 </script>
16 <?php
17}
18add_action( 'wp_footer', 'sdstudio_cf7_reinit_in_popup' );

After saving, open the popup with the form and test it: fill in the required fields incorrectly and click "Submit." Validation messages should appear instantly without a reload. Then submit the form correctly and confirm that the success message also displays inside the popup.

The code waits for the elementor/popup/show event, which is guaranteed to fire after the popup content has rendered. Then querySelectorAll finds all CF7 forms in the current DOM, and wpcf7.init() forcibly attaches AJAX validation and submission to each one. The typeof wpcf7 !== 'undefined' check guards against errors if CF7 hasn't loaded for some reason.

Solution 2: tracking clicks on the popup button (alternative method)

This approach is posted in Elementor's GitHub discussion #7798 by user @drinkmaker. Instead of tracking popup opening, it monitors clicks on a button or link with href='#elementor-action', which is exactly how Elementor triggers popups.

The setTimeout(..., 800) delay allows time for the popup's appearance animation before the code finds and initializes the form. The .elementor marker prevents reinitializing the same form twice.

1/**
2 * Alternative initialization of CF7 in Elementor popups.
3 * Source: https://github.com/elementor/elementor/issues/7798 (drinkmaker)
4 */
5function sdstudio_elementor_cf7_alt_init() {
6 ?>
7 <script type='text/javascript'>
8 jQuery( document ).ready( function() {
9
10 jQuery( document ).on( 'click', "a[href='#elementor-action']", function() {
11
12 setTimeout( function() {
13
14 jQuery( '.elementor-popup-modal form.wpcf7-form:not(.elementor)' ).each( function( index ) {
15 wpcf7.initForm( jQuery( this ) );
16 jQuery( this ).addClass( 'elementor' );
17 });
18
19 }, 800 );
20
21 });
22
23 });
24 </script>
25 <?php
26}
27add_action( 'wp_footer', 'sdstudio_elementor_cf7_alt_init' );

Which method to choose: the first one (using the elementor/popup/show event) is preferable because it relies on Elementor's documented API, is cleaner, and doesn't depend on timeouts. The second one has been battle-tested in production for years and serves as a reliable Plan B if the first doesn't work for some reason.

Optional: reset form on reopen

When a user closes the popup and opens it again, the form fields remain filled in. This is confusing: it's unclear whether the form was submitted or not. A small addition to the first solution fixes this:

1/**
2 * Reset CF7 form each time an Elementor popup opens.
3 */
4function sdstudio_cf7_reset_on_popup_open() {
5 ?>
6 <script>
7 jQuery( document ).on( 'elementor/popup/show', function() {
8 jQuery( '.wpcf7 form' ).each( function() {
9 this.reset();
10 jQuery( this ).find( '.wpcf7-not-valid' ).removeClass( 'wpcf7-not-valid' );
11 jQuery( this ).find( '.wpcf7-response-output' ).hide();
12 jQuery( this ).find( '.wpcf7-not-valid-tip' ).remove();
13 });
14 });
15 </script>
16 <?php
17}
18add_action( 'wp_footer', 'sdstudio_cf7_reset_on_popup_open' );

The function resets field values with reset(), removes CSS classes from invalid fields, hides submission messages, and removes validation tips. The user always sees a clean form.

Optional: close popup after successful submission

After a successful submit, it makes sense to auto-close the popup after 1.5 seconds so the user has time to read the confirmation without having to hunt for the close button:

1/**
2 * Auto-close Elementor popup after successful CF7 submission.
3 */
4function sdstudio_close_popup_on_cf7_success() {
5 ?>
6 <script>
7 document.addEventListener( 'wpcf7mailsent', function() {
8 setTimeout( function() {
9 jQuery( '.dialog-close-button' ).trigger( 'click' );
10 }, 1500 );
11 }, false );
12 </script>
13 <?php
14}
15add_action( 'wp_footer', 'sdstudio_close_popup_on_cf7_success' );

The wpcf7mailsent event fires when the server confirms the email has been sent. The 1500 ms delay gives the user time to read the success message. Clicking .dialog-close-button uses Elementor's standard close button. Unlike attempts to call the popup API directly, this method is stable across all versions.

Debugging checklist

If the form still reloads the page after adding the code, go through these steps:

  • Browser console. Open DevTools (F12 → Console) and check for red JavaScript errors. A common cause is jQuery not loading or conflicting with another plugin.

  • Caching. Plugins like WP Rocket, Autoptimize, or host-level cache can minify and combine scripts. Temporarily disable aggressive JS optimization and test again.

  • jQuery in noConflict mode. If a theme or plugin wraps jQuery in noConflict, replace jQuery with $ using an appropriate wrapper, or use the full jQuery form.

  • Popup ID. Make sure the form is inside the exact popup triggered by a button with href='#elementor-action'. For popups opened via other trigger types (for example, on a timer), the first method with elementor/popup/show is more reliable.

  • Plugin conflict. Deactivate other plugins one by one and test, especially those that add their own validation scripts or modify form behavior.

  • CF7 version. These solutions have been tested on Contact Form 7 version 5.7+ and Elementor Pro 3.5+. If your CF7 version is below 5.7, the wpcf7.init() function may be named differently; update the plugin.

⁉️🤔 Frequently asked questions

Why does CF7 work on a regular page but break in a popup?

When a regular page loads, the DOM is already built, and CF7 has time to initialize all forms. Elementor's popup loads content asynchronously, after CF7 has finished its work. The form ends up in the DOM but without a bound JavaScript handler. That's why checking "it works on a separate page" doesn't help: the loading conditions are fundamentally different. The solution is always forced reinitialization when the popup opens, regardless of whether the form works elsewhere.

Can I get by without code, using a plugin or a setting?

There's no ready-made plugin that lets you "check a box and it just works" for this bug. The issue lies at the intersection of two independent products (Elementor and CF7), and each one works correctly on its own. Third-party add-ons like WPB Popup for Contact Form 7 solve the problem differently: they create their own popups rather than fixing Elementor. The code above is the minimum necessary intervention. You add it once to functions.php, and it doesn't require updates when new versions of CF7 or Elementor come out.

The first method didn't work. What should I check before switching to the second?

Check three things. First: the elementor/popup/show event is available starting from Elementor Pro 2.7; if your version is lower, use the second method right away. Second: open the console and type typeof wpcf7; if it's undefined, the CF7 plugin hasn't loaded its JavaScript (look for errors or conflicts). Third: make sure the form inside the popup has the .wpcf7 class; without it, the querySelectorAll('.wpcf7 form') selector won't find anything. In the vast majority of cases, the first method works immediately. If not, use the second: it has been proven on hundreds of sites over the years.

Do I need to add all three snippets or is one enough?

The first snippet (reinitialization) is the essential minimum. The second is an alternative; add it only if the first didn't solve the problem. Form reset and popup auto-close are optional enhancements you can add as needed: reset is useful if the popup opens multiple times during a single visit; auto-close is helpful if the popup is used for inquiries and doesn't contain lengthy confirmation text. All three snippets are independent and can work simultaneously. There are no conflicts between them.

After the fix, the form submits, but emails don't arrive. Is this related?

No, email delivery issues are a separate topic unrelated to CF7 working inside a popup. If after applying the fix the form shows a success message (green border), AJAX submission is working correctly. Emails not arriving: check SMTP settings, hosting spam filters, and the recipient address in the CF7 form settings. For reliable delivery, use an SMTP plugin like Post SMTP or FluentSMTP rather than the standard wp_mail() function, as hosts often block outgoing PHP mail.

Does this solution affect other CF7 forms on the site?

No. Both methods are isolated: the first waits for an Elementor popup to open, the second only tracks links with href='#elementor-action'. Regular CF7 forms placed on pages and in widgets continue to work normally; they are initialized on page load and remain unaffected. The only caveat: if your site uses aggressive caching with script concatenation, add the popup code to your minification exclusions to avoid double execution.

Is it worth bothering with custom code in 2026

Both plugins, Contact Form 7 and Elementor Pro, are actively developed and updated. CF7 holds its position as the most popular WordPress form plugin with over 5 million active installations. Elementor Pro is used on every fourth WordPress site.

Yet the bug described here has not been fixed at the core level of either plugin and likely never will be. The reason is architectural: CF7 is responsible for forms, Elementor is responsible for dynamic content, and initializing scripts in dynamically loaded DOM remains the developer's responsibility.

The good news: the fix is trivial, the code is added once, and it requires no maintenance. Choose the first method (the elementor/popup/show event): it's the cleanest, and you can forget about the problem. If the form in the popup is used for critical lead-generation scenarios, add the field reset and auto-close as well: the user experience will improve noticeably.

Watch the video above: it shows the full process of setting up a popup with Contact Form 7 in Elementor. This visual step-by-step guide complements the snippets provided here and helps you avoid mistakes during assembly.