Back to Tips & Tricks

JetFormBuilder Success Message Popup: Custom Popup for Form Messages

Atif JanPublished on Updated
Web form with a centered success message popup over a blurred page, illustrating a JetFormBuilder success message popup

To show a JetFormBuilder success message in a popup, switch the form to AJAX submit and add a footer snippet. The snippet listens for JetFormBuilder's own jet-form-builder/ajax/on-success event and copies the message into a modal. It catches errors too: jet-form-builder/ajax/processing-error (3.5.1+) fires when the server answers with an error such as Submit failed, and jet-form-builder/ajax/on-fail (3.4.7+) fires when the request itself fails. It also works with page-reload forms. The full CSS, HTML and jQuery code is below, ready to paste into a snippets plugin.

This fixes a common problem with long forms. After the visitor clicks submit, the inline message renders outside the viewport, and they never see whether the form went through. The code is written for JetFormBuilder 3.6.7, the current version. It needs no popup plugin or page builder.

The JetPopup option and its limits

JetFormBuilder's guide How to Customize the Confirmation Message has a section called "Adjust a Pop-up to Display the Confirmation Message". It needs JetPopup (the guide uses an Elementor pop-up), the free Forms pop-up notification add-on from Crocoblock's DevTools page, a Show Popup post-submit action, and a short CSS and JavaScript snippet that use the pop-up's ID. If you already build pop-ups in JetPopup and only care about the success message, start there.

The same guide notes that the error message is still displayed as the default inline message. Use the custom snippet when you want:

  • Success and error messages in the same popup.
  • One popup for every form on the site, without building a popup per form.
  • No dependency on JetPopup, Elementor or another popup tool.

A third option is to skip the message and send visitors to a thank-you page with the Redirect to Page post-submit action.

Before you start

  1. Update JetFormBuilder. Recent releases fixed security issues, including "Shortcode injection in form status messages" in 3.6.5.2 and a reflected DOM XSS through HTML macros in 3.6.6.1. Run the latest version before you add front-end code that works with form messages.
  2. Pick the submit type. JetFormBuilder can submit by AJAX or by page reload, and you choose this in the form's settings (see Form Settings). AJAX gives the best result because the popup opens the moment the server responds. With page reload, the snippet opens the popup after the page has reloaded.
  3. Set the message text. The popup shows whatever text the form returns. Edit it per form in the right-side settings panel under General Messages Settings, for example by replacing "Form successfully submitted" with your own wording, then click Save. The error messages there include "Submit failed", "Validation error", "Captcha validation failed" and "Entered an invalid email". The Message Success and Message Error sections of the Block Style menu only style the inline messages. The popup uses its own CSS.
  4. Remove older popup snippets. If you installed another message-popup script before, especially one that loads its own copy of jQuery from code.jquery.com, deactivate it. Two scripts mean two popups, and a second jQuery copy can break other plugins.

Add the popup snippet

The snippet is one HTML block with three parts: CSS for the overlay and the message box, the popup markup, and a jQuery script that opens and closes it. It uses the jQuery that WordPress already loads, so do not add another jQuery script tag.

  1. In your WordPress dashboard, open your snippets plugin (or your theme's custom code area for footer scripts) and create a new HTML snippet.
  2. Paste the full code from the box below.
  3. Set the snippet to output in the site footer, on the front end, on every page. The listener does nothing until a form fires an event, so loading it site-wide costs almost nothing. It also covers forms that appear later, such as forms inside AJAX popups.
  4. Leave TARGET_CLASS empty to use the popup for all forms, or set it as described in the customization section below.
  5. Save and activate the snippet. Then clear your page cache and any JS minification cache.

Paste this into a new HTML snippet that loads in the site footer:

Code

<!-- BoostMyCroco: JetFormBuilder success and error message popup. Load in the site footer. -->
<style>
  /* Hide the inline success message only while the popup script is running.
     Error messages stay inline too, so visitors still see what to fix after closing the popup. */
  .bmc-popup-on .jet-form-builder-message--success {
    display: none;
  }

  #bmc-msg-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(17, 24, 39, 0.45);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    z-index: 100000;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
  }

  #bmc-msg-popup {
    position: fixed;
    top: 45%;
    left: 50%;
    transform: translate(-50%, -50%);
    box-sizing: border-box;
    width: 480px;
    max-width: 90%;
    padding: 32px 28px 24px;
    background: #ffffff;
    border-top: 6px solid #6b7280;
    border-radius: 12px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
    text-align: center;
    z-index: 100001;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, top 0.3s ease, visibility 0.3s ease;
  }

  #bmc-msg-overlay.is-open {
    opacity: 1;
    visibility: visible;
  }

  #bmc-msg-popup.is-open {
    opacity: 1;
    visibility: visible;
    top: 50%;
  }

  #bmc-msg-popup.is-success {
    border-top-color: #16a34a;
  }

  #bmc-msg-popup.is-error {
    border-top-color: #dc2626;
  }

  #bmc-msg-content {
    margin: 0 0 20px;
    font-size: 18px;
    line-height: 1.5;
    color: #1f2937;
  }

  #bmc-msg-close {
    padding: 10px 28px;
    border: 0;
    border-radius: 6px;
    background: #0f766e;
    color: #ffffff;
    font-size: 16px;
    cursor: pointer;
  }

  #bmc-msg-close:hover,
  #bmc-msg-close:focus-visible {
    background: #115e59;
    outline: 2px solid #115e59;
    outline-offset: 2px;
  }
</style>

<div id="bmc-msg-overlay"></div>
<div id="bmc-msg-popup" role="dialog" aria-modal="true" aria-label="Form message" aria-describedby="bmc-msg-content">
  <p id="bmc-msg-content" aria-live="polite"></p>
  <button type="button" id="bmc-msg-close">Close</button>
</div>

<script>
(function () {
  function bmcMessagePopup($) {
    // Leave empty to use the popup for every JetFormBuilder form.
    // To limit it, enter a CSS class (without the dot) added to a wrapper around the form.
    var TARGET_CLASS = '';
    var DEFAULT_SUCCESS = 'Form successfully submitted';
    var DEFAULT_ERROR = 'Something went wrong. Please check the form and try again.';

    var $overlay = $('#bmc-msg-overlay');
    var $popup = $('#bmc-msg-popup');
    var $content = $('#bmc-msg-content');
    var $close = $('#bmc-msg-close');
    var lastFocus = null;

    if (!$popup.length) {
      return;
    }

    // Inline success messages are hidden only when this class is present (see CSS).
    $('html').addClass('bmc-popup-on');

    function isTarget($el) {
      if (!TARGET_CLASS) {
        return true;
      }
      return $el.length > 0 && $el.closest('.' + TARGET_CLASS).length > 0;
    }

    // Turns a message that may contain HTML into plain text without running any of it.
    function toText(value) {
      if (typeof value !== 'string' || value === '') {
        return '';
      }
      var doc = new DOMParser().parseFromString(value, 'text/html');
      return $.trim(doc.body.textContent || '');
    }

    function openPopup(message, type) {
      if (!$popup.hasClass('is-open')) {
        lastFocus = document.activeElement;
      }
      $content.text(message);
      $popup.removeClass('is-success is-error').addClass('is-' + type);
      $overlay.addClass('is-open');
      $popup.addClass('is-open');
      setTimeout(function () {
        $close.trigger('focus');
      }, 50);
    }

    function closePopup() {
      if (!$popup.hasClass('is-open')) {
        return;
      }
      $popup.removeClass('is-open');
      $overlay.removeClass('is-open');
      if (lastFocus && typeof lastFocus.focus === 'function') {
        lastFocus.focus();
      }
      lastFocus = null;
    }

    $close.on('click', closePopup);
    $overlay.on('click', closePopup);

    $(document).on('keydown', function (e) {
      if (!$popup.hasClass('is-open')) {
        return;
      }
      if (e.key === 'Escape' || e.key === 'Esc') {
        closePopup();
      } else if (e.key === 'Tab') {
        // The Close button is the only control, so keep focus inside the popup.
        e.preventDefault();
        $close.trigger('focus');
      }
    });

    // AJAX submit: success. Arguments are (event, response, $form).
    $(document).on('jet-form-builder/ajax/on-success', function (event, response, form) {
      var $form = $(form);
      if (!isTarget($form)) {
        return;
      }
      var message = toText(response && response.message);
      if (message) {
        openPopup(message, 'success');
        return;
      }
      // JetFormBuilder inserts the inline message right after this event fires.
      setTimeout(function () {
        var text = $.trim($form.find('.jet-form-builder-message--success').last().text());
        openPopup(text || DEFAULT_SUCCESS, 'success');
      }, 150);
    });

    // AJAX submit: failure. on-fail needs JetFormBuilder 3.4.7+, processing-error needs 3.5.1+.
    // Their arguments are not documented, so the form and message are detected defensively.
    $(document).on('jet-form-builder/ajax/on-fail jet-form-builder/ajax/processing-error', function () {
      var $form = $();
      var message = '';

      for (var i = 1; i < arguments.length; i++) {
        var arg = arguments[i];
        if (!arg || arg instanceof Error) {
          continue;
        }
        if (arg.jquery || arg.nodeType === 1) {
          $form = $(arg).first();
        } else if (typeof arg === 'object' && typeof arg.message === 'string') {
          message = toText(arg.message);
        } else if (typeof arg === 'object' && arg.responseJSON && typeof arg.responseJSON.message === 'string') {
          message = toText(arg.responseJSON.message);
        }
      }

      if (!isTarget($form)) {
        return;
      }

      if (message) {
        openPopup(message, 'error');
        return;
      }

      // No message in the arguments: read the error JetFormBuilder printed.
      setTimeout(function () {
        var $scope = $form.length ? $form : $(document);
        var text = $.trim($scope.find('.jet-form-builder-message--error').last().text());
        openPopup(text || DEFAULT_ERROR, 'error');
      }, 150);
    });

    // Page-reload submit: show the message JetFormBuilder printed after the reload.
    var $printed = $('.jet-form-builder-message--success, .jet-form-builder-message--error').filter(function () {
      return $.trim($(this).text()) !== '' && isTarget($(this));
    }).first();

    if ($printed.length) {
      openPopup(
        $.trim($printed.text()),
        $printed.hasClass('jet-form-builder-message--error') ? 'error' : 'success'
      );
    }
  }

  if (window.jQuery) {
    jQuery(bmcMessagePopup);
  } else {
    document.addEventListener('DOMContentLoaded', function () {
      if (window.jQuery) {
        jQuery(bmcMessagePopup);
      }
    });
  }
})();
</script>

How the code works

  • Success: the script binds jQuery( document ).on( 'jet-form-builder/ajax/on-success', … ), which receives (event, response, $form). response.message holds the success message, and the popup shows it. If that is empty, the script waits a moment for JetFormBuilder to insert the inline message, then reads the last .jet-form-builder-message--success element inside the submitted form.
  • Errors: it listens to jet-form-builder/ajax/on-fail (added in 3.4.7) and jet-form-builder/ajax/processing-error (added in 3.5.1). In the current code, processing-error passes (event, response, $form) and on-fail passes (event, jqXHR, textStatus, errorThrown, $form). These are not in the official docs, so the script still checks the arguments defensively: it looks for the form and a message, falls back to the error printed in the form, and finally to a generic text. Server error messages need 3.5.1 or later, and failed requests 3.4.7 or later.
  • Page reload: when the page loads, the script looks for a non-empty success or error message and opens the popup with it.
  • Safe output: the message is converted to plain text with DOMParser, which does not run scripts or load images. It is inserted with .text(), so HTML inside a message cannot run code. Only the message of the form that was submitted is used, not every message on the page.
  • Inline messages: the script adds a bmc-popup-on class to the html element, and the CSS hides inline success messages only when that class is present. If the script fails to load, visitors still see the normal inline message. Error messages stay inline on purpose, so after closing the popup the visitor can see what to fix.
  • Accessibility: the box has role="dialog" and aria-modal="true", and the message area is aria-live="polite". Focus moves to the Close button and stays there while the popup is open. Esc, a click on the overlay or the Close button closes it, and focus returns to where it was.

The --success and --error modifier classes come from JetFormBuilder's front-end markup as used in community scripts, not from the official documentation. If a future version renames them, the AJAX success popup still works because it reads response.message first.

Change the text, look and target forms

Button text, colors and size

  • Button text: change Close inside the <button> tag to any label you like.
  • Message text: edit it in the form's General Messages Settings, not in the code. DEFAULT_SUCCESS and DEFAULT_ERROR are only fallbacks.
  • Colors: the top border uses #16a34a for success and #dc2626 for errors. The button uses #0f766e.
  • Size: the box is 480px wide and never wider than 90% of the screen, so it fits phones without extra media queries.

Use the popup for some forms only

  1. Add a CSS class such as bmc-popup-messages to a wrapper around the form, for example the Group block, Elementor container or Bricks element that holds it. If you also want a stable identifier on the form itself, see how to add a form ID to JetFormBuilder.
  2. In the script, set the class name without the dot:
    var TARGET_CLASS = 'bmc-popup-messages';
  3. Change the first CSS rule so inline success messages are hidden only in those forms:
    .bmc-popup-on .bmc-popup-messages .jet-form-builder-message--success {
      display: none;
    }

Hide inline error messages too

JetFormBuilder's own rule for hiding the inline message is .jet-form-builder-message { display: none; }, added in WordPress Dashboard > Appearance > Customize > Additional CSS. To keep the fallback behavior, replace the first rule in the snippet instead:

.bmc-popup-on .jet-form-builder-message {
  display: none;
}

Test an error first. If the error popup does not open on your setup, this rule leaves visitors with no error message at all.

Test it

  1. Open the page with the form in a private browser window, so you get the same cached page a visitor gets.
  2. Fill in the form and submit it. The page behind should blur, and a white box with a green top border should slide in with your success message. The Close button should have focus.
  3. Close it three ways: the Close button, a click on the overlay and the Esc key.
  4. Trigger an error your form can actually produce, such as a failed captcha or a server-side rule. The box should open with a red top border, and the error should also stay visible next to the form.
  5. Open the browser console and check that the snippet throws no errors.

Troubleshooting

  • The popup never opens. View the page source and search for bmc-msg-popup. If it is missing, the snippet is not active, not set to the footer, or the page cache is serving old HTML. If it is there, check whether an optimization plugin delays or combines jQuery. Exclude jQuery and this snippet from JS delay and minification, then clear the caches.
  • The popup opens twice or flickers. jQuery is probably loaded twice, or an older popup snippet is still active. Remove the extra jQuery script tag and deactivate the old snippet.
  • The popup sits behind the header or another popup. Raise the z-index values of #bmc-msg-overlay and #bmc-msg-popup above your theme's header and popup layers.
  • The popup opens on page load or after a refresh. With page-reload submit, refreshing the result page can print the message again. If your forms only use AJAX, delete the block under the comment "Page-reload submit".
  • The success message still shows inline. Inspect the message in your browser's developer tools. If the classes differ from jet-form-builder-message--success, update the CSS selector.
  • You see "nonce _failed" or "One or more fields have an error. Please check and try again." This comes from the form, not the popup. Crocoblock's troubleshooting guide suggests turning off the form's Enable form safety toggle, disabling caching on form pages, clearing the browser cache, or shortening the cache time to under 12 hours.
  • Forms inside JetPopup or Bricks AJAX popups misbehave. Update JetFormBuilder. Recent releases fixed forms in JetPopup AJAX popups not initializing correctly (3.6.x), form styles in dynamic popups (3.6.6) and Bricks AJAX popup compatibility (3.5.6). The snippet listens on the document, so it picks up these forms once they initialize.

With AJAX submit and this snippet in the footer, JetFormBuilder success and server error messages appear where visitors can't miss them, and errors stay next to the form for corrections. For more front-end improvements, see how to add a phone field with country flags. If you'd rather have someone build and style it for you, post a request on BoostMyCroco.

Frequently Asked Questions

Does the JetFormBuilder message popup work with page reload submit?
Yes. With page reload, the snippet checks the page after it loads and opens the popup with the success or error message JetFormBuilder printed. AJAX submit still gives a better experience because the popup opens the moment the server responds, without a reload.
Can I use JetPopup instead of custom code to show the success message?
Yes, if you use JetPopup with Elementor. JetFormBuilder's guide shows how: install the free Forms pop-up notification add-on, add a Show Popup post-submit action, and add the guide's CSS and script with your pop-up's ID. That method covers the success message only; errors still appear inline. Use the custom snippet if you want errors in the popup too or don't want to build a popup per form.
How do I show the popup for only one JetFormBuilder form?
Add a CSS class such as bmc-popup-messages to a wrapper around that form and set TARGET_CLASS to that class name, without the dot. Then scope the first CSS rule to the same class so inline success messages stay visible on your other forms.
How do I change the Close button text or the success message?
Change the word Close inside the button tag of the snippet. The success and error texts come from each form's General Messages Settings in the right-side settings panel, so edit them there and click Save.
Why don't field validation errors appear in the popup?
When a field is empty or invalid, JetFormBuilder usually marks it next to the field and the form is not sent, so no submit event reaches the popup script. The popup shows messages returned after the form is sent, such as success or Submit failed.

Sources

  1. How to Сustomize the Сonfirmation Message — JetFormBuilder (jetformbuilder.com)
  2. JetFormBuilder: How to Redirect Form to Thank You Page (jetformbuilder.com)
  3. Form Settings 一 JetFormBuilder l Crocoblock (jetformbuilder.com)
  4. Form Shows Error on Front End During Submission 一 JetEngine (crocoblock.com)
  5. JetFormBuilder submission feedback auto-scroll · GitHub (gist.github.com)
  6. JetFormBuilder change display message to Popup · GitHub (gist.github.com)
  7. JetFormBuilder JS · GitHub (gist.github.com)
  8. JetFormBuilder — Dynamic Blocks Form Builder (wordpress.org)
  9. JetPopup trigger open / close · GitHub (gist.github.com)
  10. JetFormBuilder Plugin: General Messages Settings (jetformbuilder.com)
  11. jet form builder popup message - jetformbuilder · GitHub (gist.github.com)

Related tips