Written by me@grafxflow
30 Apr, 2017
7
10,259
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):
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.
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(); ?>
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();
});
});
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' );
?>
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.
24 Nov, 2013
07 Nov, 2012
01 Aug, 2015
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!
Follow11 Jul, 2023
21 Jun, 2023
Views: 166,095
Views: 40,208
Views: 36,920
Views: 33,515
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.