There's a huge lie being told on the internet and everyone seems to believe it: Everyone hates pop-ups. It's true, they can be abused — and they often are — but that doesn't mean they don't work. So let's build our own custom WordPress email form optin pop-up.

Note: What is an optin pop-up? It's usually a simple form that appears when the visitor either tries to leave your site or when they engage with your site in a way that makes sense for you to have it appear, in order to gather their email address. In exchange you can offer special discounts, ebooks, coupons, etc. to your new subscribers. This is also referred to as a Lead Magnet; the bribe used in exchange for an email address.

Let's assume for the purpose of this tutorial, that you're selling a product worth $50 on your website and have 1000 daily visitors. Your bounce/abandonment rate is roughly 50%, which means 500 of those visitors are lost forever.

With exit intent detection (more on that shortly), you could try and get their email address for future follow up and promotions. Assuming 10% of your almost lost visitors give it to you, this means you gather 50 emails per day, roughly 1500 per month.

With 1500 emails per month, you can easily achieve 5% conversion rate (with some smart e-mail marketing), which amounts to 75 additional sales per month, and that results in $3,750 of additional monthly revenue!

Still think pop-ups are a bad idea?

Well, don't just take my word for it - folks at Crazy Egg did a thorough analysis of optin forms and visitor behaviour and discovered suprisingly awesome results! And Chris Lema agrees as well!

This tutorial is not going to be about why (refer to the Crazy Egg article above for that), but about how: We're going to build our own custom optin form with exit intent detection which will — once submitted — send the visitors' information to an external provider, such as MailChimp.

Step 1: Download and configure exit intent detection script

Exit intent is a point in your visitor's journey through your website when they decide to leave for any reason and we need to detect it. Luckily, it's quite easy to do that with a little help from a JavaScript solution, called Ouibounce.

Download the ouibounce.js and save it into your js directory inside your theme, then create another JavaScript file ouibounce-config.js, which you will use to configure the behaviour of your pop-up.

Paste the following code in this newly created file:

jQuery(function($) {
  "use strict";

  ouibounce($('#js-optin-wrap')[0], {
    // Uncomment the line below if you want the modal to appear every time
    // More options here:
    aggressive: true,
    sitewide: true,

  $("#js-optin-submit").click(function(event) {


    var data = {
      'action': 'process_optin_submission',
      'nonce': window.OuibounceVars.nonce,
      'email': $('#js-optin-email').val()

    $.post(window.OuibounceVars.ajaxUrl, data, function(response) {
      console.log('Server returned:', response);

      // Handle the response (take care of error reporting!)

  $('.js-optin-close').on('click', function(event) {

Ouibounce doesn't provide any form functionality — it just detects exit intent and shows whichever element you pass to it as the first argument, which is why you need to configure your form's behaviour with jQuery and a dash of ajax (Read the official documentation to refresh your knowledge about how ajax works in WordPress).

Behind the scenes, it also sets a cookie (whose expiration time you can configure), so that the form only appears once per month to a visitor - to minimize the risk of really being intrusive.

So what does the script above do? It registers a listener on your form's submit button, then makes an ajax call once that button is clicked. Upon successful response, it hides the input field and thanks the visitor for subscribing.

Note: This is a very basic example that has only one (email) field and no validation whatsoever. I'll leave it to your discretion to take care of that.

Also pay attention to the aggressive parameter, if set, it will trigger the pop-up every time a visitor tries to leave your website. This will most likely annoy your visitors, so I recommend turning it off inside the production environment!

Step 2: Create a processing script

Since you're making an ajax call you need a script that will catch and process your request, and since I'm a big fan of object oriented programming, let's create a class for that; This class will have two functions (called methods) - one for processing the request and the other for displaying our pop-up's HTML:


class Optin_Form {

  public function __construct() {
    add_action( 'wp_ajax_process_optin_submission', array( $this, 'process' ) );
    add_action( 'wp_ajax_nopriv_process_optin_submission', array( $this, 'process' ) );

  public function process() {
    if( ! wp_verify_nonce( $_POST['nonce'], 'ouibounce' ) )

    $data = array(
      'email' => $_POST['email']

    $curl = curl_init();
    curl_setopt_array( $curl, array(
      CURLOPT_HTTPHEADER => array( 'Content-Type: application/json', 'Accept: application/json' ), // -H parameter
      CURLOPT_RETURNTRANSFER => 1, // so that we can catch the response in a variable
      CURLOPT_URL => '', // The endpoint
      CURLOPT_POST => 1, // -X POST
      CURLOPT_USERPWD =>  'app_id:api_key', // -u parameter (not always needed)
      CURLOPT_POSTFIELDS => json_encode( $data ) // because we set Content-Type to JSON
    ) );

    $resp = curl_exec( $curl );
    curl_close( $curl );
    print_r( $resp );


  public function render() { ?>
    <div class="modal-cover" id="js-optin-wrap">
      <div class="modal">
        <div id="js-optin-step1">
          <h1>Hey there!</h1>
          <p>Want to be <strong>awesome</strong>? Subscribe!</p>
            <input type="text" id="js-optin-email" placeholder="Your email" />
            <input type="submit" id="js-optin-submit" value="Sign me up!" />
          <br />
          <a href="#" class="js-optin-close">No thanks.</a>
        <div id="js-optin-step2" style="display:none;">
          <h1>You've been subscribed!</h1>
          <br />
          <a href="#" class="js-optin-close">Close.</a>

Let's take a look at the render() method first, since it's the easier of the two; All it does is output your pop-up's HTML. Ideally, this would be in a separate file, but for the purpose of this tutorial, it'll do. Note the id attributes that start with js-: This is a nice convention to remind yourself that a particular selector is used for JavaScript/jQuery and not for styling.

The rendered pop-up/form has one input field (email), a submit button, and a close link. It also has two steps, the second being displayed once your form is submitted and the response is returned.

The first part of the class (the process() method) is where things get interesting; First, we check that the nonce is valid (a good practice from a security perspective) and then initiates a cURL call.

For those that don't know, cURL is the programatic equivalent of how your browser communicates with the server - you enter a URL in the address bar, which creates a request, and the server returns a response, which your browser then interprets. cURL does the same, but without the browser - it creates a request and parses the response.

While this approach can seem a bit confusing at first, most (if not all) major email marketing providers provide APIs (URLs that computers can talk to, much like WordPress's XML-RPC).

In the case of our example, I'm using a fake API service REQ|RES, but on our live solution, I'm making a call to our customer support system, Intercom.

Looking at what your code does next, you'll see a number of settings that start with CURLOPT_; These are the settings you make the cURL call with:

  • CURLOPT_HTTPHEADER sets the request headers such as what type of content you're sending and expecting.
  • CURLOPT_RETURNTRANSFER makes sure you can catch the response in a variable
  • CURLOPT_URL is well, the URL you're making a call to
  • CURLOPT_POST sets the call type to POST
  • CURLOPT_USERPWD is an optional parameter if you need to use your API credentials to connect successfully - and in most cases, you do.
  • CURLOPT_POSTFIELDS is your normalized data - in our case it's just one email address, but you could have as many fields as you like, provided the API service supports them.

With your call now configured, it's time to actually make it, so the last few lines in our method do that, then close it, print out the data (which our JavaScript is able to read) and prevent PHP from doing anything else with the wp_die().

Step 3: Load your scripts in WordPress

Now that you have all the necessary functionality in place, it's time to register all the scripts with WordPress, for which you just need to add the following code into your functions.php:

* Load Optin form class and let it render just before
require get_template_directory() . '/inc/class-optin-form.php';
$optin_form = new Optin_Form();
add_action( 'wp_footer', array( $optin_form, 'render' ) );

* Enqueue all the necessary scripts and set proper JavaScript variables
function enqueue_ouibounce() {
  wp_enqueue_script( 'ouibounce', get_stylesheet_directory_uri() . '/js/ouibounce.js', array() );
  wp_enqueue_script( 'ouibounce-config', get_stylesheet_directory_uri() . '/js/ouibounce-config.js', array( 'jquery' ) );
  wp_localize_script( 'ouibounce-config', 'OuibounceVars', array(
    'ajaxUrl' => admin_url( 'admin-ajax.php' ),
    'nonce' => wp_create_nonce( 'ouibounce' )
  ) );
add_action( 'wp_enqueue_scripts', 'enqueue_ouibounce' );

The first part of this code loads the class and instantiates it so that you can run the render() method - this outputs the pop-up HTML just before the `` closing tag.

The second part just enqueues all the JavaScripts and sets some variables via wp_localize_script().

Now before you can try this out, you also need to style your pop-up, or more importantly hide it, so it only appears once.

Step 4: Style your pop-up

Here are some basic styles for your pop-up you can use as a starting point:

.modal-cover {
  display: none;
  background: rgba(0, 0, 0, 0.75);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  padding-top: 20px;
/* ouibounce adds 'ouibounce-open' class on the <body> tag */ 
.ouibounce-open .modal-cover { display: block; }

.modal {
  width: 600px;
  margin: 0 auto;
  background: #fff;

The most important thing to note here is that .modal-cover (our pop-up wrapper class) is hidden by default and only revealed once the Ouibounce fires - when this occurs, a .ouibounce-open class is added to the `` tag, revealing the modal.

The final result then looks like this:

WordPress Email form pop-up
Our DIY WordPress email form pop-up

Caveats and conclusion

At this point you may wonder why do you need all the hosted (and some even quite pricey) solutions available in the marketplace? Well, depending on your needs you might need the following functionality that our DIY solution doesn't provide:

  • A/B testing to further improve the effectiveness of your pop-ups
  • Other types of detection (for example to show once a visitor scrolls past a certain point, or spends more time on a particular page)
  • Form builder for easily adding images, fields and buttons, all with point and click simplicity
  • Pre-made templates for you to choose from so you don't have to spend time on design
  • Multi-step forms inside pop-ups
  • Mobile support, which is getting increasingly popular
  • Automatic integration with other 3rd party services

It all comes down to your needs and preferences - for us, many of these are important but decided to integrate on our own, which takes time, and more importantly, money.

If you just want to get started quickly and are not very technical, go with OptinMonster

A lie told on the internet: Everyone hates pop-ups. But for business, they make sense. And money. Click To Tweet

Since you've read the whole tutorial and I'm sure you're now eager to get started, I prepared a gist for you to grab the code and start playing with it.