WordPress logo
me@grafxflow

Written by me@grafxflow

30 Apr, 2017

7

10,166

WordPress Custom Ajax Contact Form

Most people are aware of the plugins for WordPress that help you create forms - Contact Form 7 and Ninja Forms etc. But what about building your own custom ajax contact form using the admin-ajax WordPress functions. Well here is a starter tutorial which I have created where I am guessing you already know the basics of how to create custom themes and pages in WordPress.

For this example I will be using the following extras (Note: none are WordPress plugins):

  • Bootstrap 3 for the form layout Download
  • BootstrapValidator for the form validation + this also includes jQuery 1.11.1 Download

NOTE: I have tried to add notes to the code examples as a more detailed breakdown. I have also added the CSS inside the page, but best practise would be to add them to the themes style.css file.

 Step 1:

Create the custom WordPress theme contact page

So first let's create a custom page template for the contact page. Given the below details at the start of the file it will allow us to create a contact page and select this as a custom template. As for the form I am creating the following items all of which will be required - Fullname, Email, Subject and Message.

You will also notice 2 extra elements - a hidden field called 'action' which tells WordPress what to do with the form in its functions file. Plus a container with the success message which will appear once it has validated, therefore I have added some CSS which hides it by default.

File called 'contact-page.php' and saved in your themes folder.

<?php
/**
 * The default contact page template
 * Template Name: Contact Page
 *
 * @package WordPress
 * @subpackage yourthemename
 * @since yourthemename 1.0.0
 */

get_header(); ?>

<!-- The Required CSS - better added to your themes header.php file or called within the functions.php -->
<link rel="stylesheet" id="bootstrap.min-css" href="<?php echo get_bloginfo('template_directory'); ?>/css/bootstrap/bootstrap.min.css" type="text/css" media="all" />
<link rel="stylesheet" id="style-css" href="<?php echo get_bloginfo('template_directory'); ?>/style.css" type="text/css" media="all" />

<!-- Hide the form submit success_message by default -->
<style type="text/css">
    #success_message {
        display:none;
    }
</style>

<!-- Start page container -->
<div class="container">
    <div class="row">
        <div class="col-xs-12">     

            <!-- Start form -->
            <form action="#" class="form-horizontal" id="contact_form" method="post"> 

                <!-- Text input for fullname -->
                <div class="form-group">
                    <label for="fullname">Fullname*</label>
                    <input class="form-control" name="fullname" placeholder="Full Name" type="text">
                </div>

                <!-- Text input for email -->
                <div class="form-group">
                    <label for="email">Email*</label>
                    <input class="form-control" name="email" placeholder="E-Mail Address" type="text">
                </div>

                <!-- Text input for subject -->
                <div class="form-group">
                    <label for="subject">Subject*</label>
                    <input class="form-control" name="subject" placeholder="Subject" type="text">
                </div>

                <!-- Textarea input for message -->
                <div class="form-group">
                    <label for="message">Message*</label>
                    <textarea class="form-control" name="message" placeholder="Enquiry message"></textarea>
                </div>

                <!-- Submit Button -->
                <input class="btn btn-block btn-primary" type="submit" value="Submit">

                <!-- Hidden value added for WordPress ajax security and to validate function name -->
                <input type="hidden" name="action" value="yourwebsite_contactform"/>
            </form>
            <!-- End form -->

            <!-- Start success message -->
            <div class="alert alert-success" id="success_message" role="alert">Success <i class="glyphicon glyphicon-thumbs-up"></i> Thanks for contacting us, we will get back to you shortly.
            </div>
            <!-- End success message -->

        </div>
    </div>
</div>
<!-- End page container -->

<!-- The Required javascript - better added to your themes footer.php file or called within the functions.php -->
<script type="text/javascript" src="<?php echo get_bloginfo('template_directory'); ?>/js/jquery/jquery.min.js"></script>
<script type="text/javascript" src="<?php echo get_bloginfo('template_directory'); ?>/js/bootstrap/bootstrap.min.js"></script>
<script type="text/javascript" src="<?php echo get_bloginfo('template_directory'); ?>/js/bootstrapvalidator-master/dist/js/bootstrapValidator.min.js"></script>

<?php get_footer(); ?>

 Step 2:

Add the forms javascript validation script

Now lets create the javascript file using 'bootstrapValidator' that will validate the form. And once successful will post it via ajax to the WordPress 'admin-ajax.php' file.

File called 'ajax-contactus-script.js' and saved in your themes folder.

// JavaScript Document
jQuery(document).ready(function ($) {

    // Allocate the bootstrapValidater to our form based on the forms id
    $('#contact_form').bootstrapValidator({

        // Set the icons used for the default error/required messages
        feedbackIcons: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        // Sets the validation for each field on the form
        fields: {
            // The fullname requires at least 2 characters and can't be empty
            fullname: {
                validators: {
                    stringLength: {
                        min: 2,
                    },
                    notEmpty: {
                        message: 'Please supply your fullname'
                    }
                }
            },
            // The email can't be empty and must be a valid email address @ etc
            email: {
                validators: {
                    notEmpty: {
                        message: 'Please supply your email address'
                    },
                    emailAddress: {
                        message: 'Please supply a valid email address'
                    }
                }
            },
            // The phone can't be empty
            phone: {
                validators: {
                    notEmpty: {
                        message: 'Please supply your phone number'
                    }
                }
            },
            // The message requires at least 10 characters and can only contain a max of 200 charcters and can't be empty
            message: {
                validators: {
                    stringLength: {
                        min: 10,
                        max: 200,
                        message: 'Please enter at least 10 characters and no more than 200'
                    },
                    notEmpty: {
                        message: 'Please add a message'
                    }
                }
            }
        }
    })
    .on('success.form.bv', function (e) {
        // Once validated it will be passed to the WordPress admin-ajax.php url
        var $form = $(e.target);
        $.ajax({
            type: 'POST',
            url: wp_ajax.url,
            data: $form.serialize(),
            dataType: 'json',
            success: function (response) {
                // On a successful response slides and fades in the success message
                $('#success_message').slideDown({
                    opacity: "show"
                }, "slow");
                // Fade out the form
                $('#contact_form').fadeOut();
                // Reset all the values in the form to empty
                $('#contact_form').bootstrapValidator("resetForm", true);
            }
        });

        // Prevents the browser window refreshing
        e.preventDefault();
    });
});

 Step 3:

Now lets process our custom contact form with some functions in the WordPress 'functions.php' file. This will use ajax via the admin-ajax.php.

This file should already exists and is called 'functions.php' and saved in your themes folder. What it contains are the functions which will process the form and send an email to the relevant email account. Also I have made a separate html email template which make them easier to design.

IMPORTANT: Note the usage of the 'yourwebsite_contactform' hidden 'action' value used in the form and where it is used in the functions below. These must match exactly.

<?php
// Function for handling the contact form
function yourwebsite_contactform_init() {

    // This calls the javascript we just created for the form validation
    wp_localize_script( 'ajax-contactus-script', 'wp_ajax', array(
        'url' => admin_url( 'admin-ajax.php' ),
        'nonce' => wp_create_nonce( "ajax_nonce" ) // this is a unique token to prevent form hijacking
    ) );

    // Enable any admin to run ajax_login() in AJAX and POST the form
    add_action( 'wp_ajax_yourwebsite_contactform', 'yourwebsite_contactform_process' );

    // Enable any user with no privileges to run ajax_login() in AJAX and POST the form
    add_action( 'wp_ajax_nopriv_yourwebsite_contactform', 'yourwebsite_contactform_process' );
}

// Initiate the ajax enquiry form and add the validation javascript file
add_action( 'init', 'yourwebsite_contactform_init' );
add_action( 'wp_enqueue_scripts', 'yourwebsite_contactform_init' );

// Function to send the email using the email template
function yourwebsite_contactform_process() {
    $to = array('your@emailaccount.com');
    // If you want to send to several emails just add to the array like below
    // $to = array( 'your@emailaccount.com', 'another@emailaccount.com' );
    $subject = 'Email subject title';

    // This is the way to transfer the form $_POST values to the email template
    $postdata = http_build_query( $_POST );
    $opts = array( 'http' =>
        array(
            'method' => 'POST',
            'header' => 'Content-type: application/x-www-form-urlencoded',
            'content' => $postdata
        )
    );
    $content = stream_context_create( $opts );

    // Load the email template and create the email content
    $message = file_get_contents( get_bloginfo( 'template_directory' ) . '/email-template/contact-form.php', false, $content );

    // Set the email header which contains the forms fullname and the email address
    $headers = 'From: ' . trim( $_POST[ 'fullname' ] ) . ' <' . trim( $_POST[ 'email' ] ) . '>' . "\r\n";

    // Now loop through the $to email accounts
    // If you don't do this it will send the email as a group send (i.e. each email account will see all the other email accounts)
    foreach ( $to as $email_address ) {
        wp_mail( $email_address, $subject, $message, $headers );
    }

    // return the value of 1 to show it has been successful
    // The form needs to return this value to confirm the email has been sent
    echo '1';
    die();
}

// Set the default email type as html instead of text only
function yourwebsite_contactform_set_content_type(){
    return "text/html";
}
add_filter( 'wp_mail_content_type','yourwebsite_contactform_set_content_type' );
?>

 Step 4:

Create the email template to send your contact form details

And now the last part to create a html layout which will embed the contact form details and send it to your email account. We simply output the form values in a table form.

File called 'contact-form.php' and saved inside a folder called 'email-template' in your themes folder.

<!doctype html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Responsive Email Template</title>
    <style>
        /* -------------------------------------
          GLOBAL RESETS
      ------------------------------------- */
        img {
            border: none;
            -ms-interpolation-mode: bicubic;
            max-width: 100%;
        }   
        body {
            background-color: #f6f6f6;
            font-family: sans-serif;
            -webkit-font-smoothing: antialiased;
            font-size: 14px;
            line-height: 1.4;
            margin: 0;
            padding: 0;
            -ms-text-size-adjust: 100%;
            -webkit-text-size-adjust: 100%;
        }
        table {
            border-collapse: separate;
            mso-table-lspace: 0pt;
            mso-table-rspace: 0pt;
            width: 100%;
        }
        table td {
            font-family: sans-serif;
            font-size: 14px;
            vertical-align: top;
        }
        /* -------------------------------------
          BODY & CONTAINER
      ------------------------------------- */
        .body {
            background-color: #f6f6f6;
            width: 100%;
        }
        /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
        .container {
            display: block;
            Margin: 0 auto !important;
            /* makes it centered */
            max-width: 580px;
            padding: 10px;
            width: 580px;
        }
        /* This should also be a block element, so that it will fill 100% of the .container */
        .content {
            box-sizing: border-box;
            display: block;
            Margin: 0 auto;
            max-width: 580px;
            padding: 10px;
        }
        /* -------------------------------------
          HEADER, FOOTER, MAIN
      ------------------------------------- */
        .main {
            background: #fff;
            border-radius: 3px;
            width: 100%;
        }
        .wrapper {
            box-sizing: border-box;
            padding: 20px;
        }
        .footer {
            clear: both;
            padding-top: 10px;
            text-align: center;
            width: 100%;
        }
        .footer td,
        .footer p,
        .footer span,
        .footer a {
            color: #999999;
            font-size: 12px;
            text-align: center;
        }
        /* -------------------------------------
          TYPOGRAPHY
      ------------------------------------- */
        h1,
        h2,
        h3,
        h4 {
            color: #000000;
            font-family: sans-serif;
            font-weight: 400;
            line-height: 1.4;
            margin: 0;
            Margin-bottom: 30px;
        }
        h1 {
            font-size: 35px;
            font-weight: 300;
            text-align: center;
            text-transform: capitalize;
        }
        p,
        ul,
        ol {
            font-family: sans-serif;
            font-size: 14px;
            font-weight: normal;
            margin: 0;
            Margin-bottom: 15px;
        }
        p li,
        ul li,
        ol li {
            list-style-position: inside;
            margin-left: 5px;
        }
        a {
            color: #3498db;
            text-decoration: underline;
        }
        /* -------------------------------------
          RESPONSIVE AND MOBILE FRIENDLY STYLES
      ------------------------------------- */
        @media only screen and (max-width: 620px) {
            table[class=body] h1 {
                font-size: 28px !important;
                margin-bottom: 10px !important;
            }
            table[class=body] p,
            table[class=body] ul,
            table[class=body] ol,
            table[class=body] td,
            table[class=body] span,
            table[class=body] a {
                font-size: 16px !important;
            }
            table[class=body] .wrapper,
            table[class=body] .article {
                padding: 10px !important;
            }
            table[class=body] .content {
                padding: 0 !important;
            }
            table[class=body] .container {
                padding: 0 !important;
                width: 100% !important;
            }
            table[class=body] .main {
                border-left-width: 0 !important;
                border-radius: 0 !important;
                border-right-width: 0 !important;
            }
            table[class=body] .btn table {
                width: 100% !important;
            }
            table[class=body] .btn a {
                width: 100% !important;
            }
            table[class=body] .img-responsive {
                height: auto !important;
                max-width: 100% !important;
                width: auto !important;
            }
        }
        /* -------------------------------------
          PRESERVE THESE STYLES IN THE HEAD
      ------------------------------------- */
        @media all {
            .ExternalClass {
                width: 100%;
            }
            .ExternalClass,
            .ExternalClass p,
            .ExternalClass span,
            .ExternalClass font,
            .ExternalClass td,
            .ExternalClass div {
                line-height: 100%;
            }
        }
    </style>
</head>

<body class="">
    <table border="0" cellpadding="0" cellspacing="0" class="body">
        <tr>
            <td> </td>
            <td class="container">
                <div class="content">

                    <!-- START CENTERED WHITE CONTAINER -->
                    <span class="preheader">Yourwebsite Contact Form.</span>
                    <table class="main">

                        <!-- START MAIN CONTENT AREA -->
                        <tr>
                            <td class="wrapper">
                                <table border="0" cellpadding="0" cellspacing="0">
                                    <tr>
                                        <td>
                                            <table border="0" cellpadding="0" cellspacing="0">
                                                <tbody>
                                                    <tr>
                                                        <td><b>Name:</b></td>
                                                        <td><?php echo trim($_POST['fullname']);?></td>
                                                    </tr>
                                                    <tr>
                                                        <td><b>Email:</b></td>
                                                        <td><?php echo trim($_POST['email']);?></td>
                                                    </tr>
                                                    <tr>
                                                        <td><b>Subject:</b></td>
                                                        <td><?php echo trim($_POST['subject']);?></td>
                                                    </tr>
                                                    <tr>
                                                        <td><b>Message:</b></td>
                                                        <td><?php echo trim($_POST['message']);?></td>
                                                    </tr>
                                                </tbody>
                                            </table>
                                            <br>
                                            Received on <b><?php echo date('d-m-Y H:m:i');?></b>.
                                        </td>
                                    </tr>
                                </table>
                            </td>
                        </tr>

                        <!-- END MAIN CONTENT AREA -->
                    </table>

                    <!-- START FOOTER -->
                    <div class="footer">
                        <table border="0" cellpadding="0" cellspacing="0">
                            <tr>
                                <td class="content-block">
                                    Copyright © <?php echo date('Y');?> - All Rights Reserved <a target="_blank" href="http://yourwebsite.com">Your Website</a>
                                </td>
                            </tr>
                        </table>
                    </div>

                    <!-- END FOOTER -->

                    <!-- END CENTERED WHITE CONTAINER -->
                </div>
            </td>
            <td> </td>
        </tr>
    </table>
</body>

</html>

I hope you have found this helpful and a nice starting point.

Add comment

7 Response

    Shabu James
    16 Jul 2018

    It was not working.

    |

    me@grafxflow
    16 Jul 2018

    What is not working? and what version of WordPress are you using?

    |

    Miguel
    02 Dec 2018

    Where can I call the form in a specific Wordpress page? i am new .

    |

    me@grafxflow
    02 Dec 2018

    Hi Miguel,

    You should be able to create a blank page and use 'Contact Page' as a template. You should find this on the bottom right of the page.

    ... or maybe look into adding a shortcode into the php.

    https://codex.wordpress.org/Function_Reference/add_shortcode

    |

    Miguel
    02 Dec 2018

    Can you update or send me the code for wordpress 4.9.8? Thanks

    |

    mj
    20 Feb 2022

    thank you for this tutorial.

    don't you need to enqueue the script in the php part?

    wp_enqueue_script( 'ajax-contactus-script' , ....

    |

    Leicester Websites
    14 Dec 2023

    Thank you for this insightful tutorial on creating a custom Ajax contact form in WordPress. It's a great resource for those who, like myself, are familiar with basic WordPress theme and page creation but are looking to expand our skills into more advanced areas.

Smart Search

133 Following
50 Followers

me@grafxflow

Hull, United Kingdom

I am a Full-stack Developer who also started delving into the world of UX/UI Design a few years back. I blog and tweet to hopefully share a little bit of knowledge that can help others around the web. Thanks for stopping by!

Follow