How and Why To Build Your Own WordPress Contact Form

How and Why To Build Your Own WordPress Contact Form

When it comes to building a contact form for WordPress from the ground up, it’s possible to do. This article covers how to build your own contact form — and the benefits of why you should.

In this article, we’ll focus on building one thing without a plugin: a WordPress contact form.

There are loads of plugins around for everything you could ever wish for in WordPress, but you may feel rewarded and secure building features from scratch.

You might be asking why bother? Well, we’ll be covering a few reasons. For example, a lot of the time people don’t want to mess around with a plugin’s UI, or the plugin does way more than you’d want it to do.

This article covers:

It’s a great learning experience, you know what’s going on under the hood and you’re gaining skills for another time.

But still…

Why Not Just Use a Plugin?

We’d be doing ourselves a disservice if we didn’t mention our free, 5-star rated Forminator plugin. It’s our form creating go-to option (and recommendation), due to its ease-of-use, amazing capabilities (e.g. eCommerce, design, etc.), and tons more.

So, before going into why NOT to use a plugin, keep in mind that we’re not saying DON’T use one. Plugins like Forminator are a great solution.

However, there are others out there that may not be. And like we hinted at earlier, learning to create your own contact form can be an awesome learning experience.

If you’d like to take the plunge and create your own, here are four good reasons why plugins can be bad for simple contact forms:

  1. Plugins pack in way more than you (should) care for
  2. Using a UI to build a simple contact form is overkill
  3. Knowing what’s under the hood is good for your knowledge, and your peace of mind
  4. Default stylesheets can kill a person from the inside.

There might be other reasons beyond this even. For example, maybe you don’t want a ton of features. You just want to keep things…simple.

But I Need All Those Features!

Really? This isn’t Web 2.0 anymore. Complex forms can scare customers. And there are a lot of them out there.

How many times have you gone to fill out a form, only to be daunted by the amount of personal data you have to put in? It happens all the time.

Users even get halfway and are then distracted by something else. People these days have a very short attention span. So in order for you to make it as easy as possible for your potential clients and customers to get in touch with you, you really should be stripping your contact form to the bone.

So What’s Absolutely Essential?

Name, email, message, and human verification. But I think you knew that already.

These four small bits of information are as simple as commenting on a blog — why would you want to make getting in touch any more difficult? I don’t think I’ve ever built a form with more information than that unless absolutely required.

If people want to leave their phone number and address, they will do so in the message. Visual cues such as asking them in a short paragraph above the form are enough!

Again, our Forminator plugin keeps things simple. But, okay, we’re going to be bold and daring and create a form from scratch. The thing is…

Surely It’s a Pain Coding Everything From scratch? Right?

Not really. If you’re patient you’ll smash through this tutorial in under half an hour. All it really consists of is:

  1. A custom template file
  2. Some PHP

Yep, that’s it. You can handle displaying the form, human verification, validation, emailing the admin, and thanking the reader for getting in touch all in one file.

Getting Started Creating Your Own Contact Form

We’re going to start with a fresh WordPress installation. If you’re unsure how educate yourself on the Codex.

To follow good practice, create a child theme with just a stylesheet and a custom page template.

In wp-content/themes, add a new folder called “contact-form.” Within that, create two new files — style.css and page-contact-us.php.

Inside style.css, paste the following:

Theme Name:     Contact Form Page Template
Theme URI:
Description:    A super simple, self-contained contact form you can drop into your existing theme.
Author:         Harley
Author URI:
Template:       twentytwelve
Version:        0.1

@import url("../twentytwelve/style.css");

By now, you should be able to go ahead and activate the theme on your test site and be ready to roll.

The next step is to create a page, aptly-named “Contact Us.” This name is special. It relates immediately to the template file we made previously called page-contact-us.php. This solution for creating a custom page template is not a catch-all, just a convention WordPress provides and I like. Page-about-us.php would automatically be used to display a page titled “About Us.” It’s kinda handy!

If you’ve created page-contact-us.php and left it empty, visiting your newly created “Contact Us” page will yield a blank page.

That’s alright though! Quickly pad it out with the following, and it will look just like a normal page again.

<?php get_header(); ?>

  <div id="primary" class="site-content">
    <div id="content" role="main">

      <?php while ( have_posts() ) : the_post(); ?>

          <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

            <header class="entry-header">
              <h1 class="entry-title"><?php the_title(); ?></h1>

            <div class="entry-content">
              <?php the_content(); ?>

              <p>FORM CODE GOES HERE</p>

            </div><!-- .entry-content -->

          </article><!-- #post -->

      <?php endwhile; // end of the loop. ?>

    </div><!-- #content -->
  </div><!-- #primary -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

Alrighty. Now we’ve got the base page in, it’s time to drop in a very simple form.

Replace the FORM CODE GOES HERE paragraph with the following:

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%20%20.error%7B%0A%20%20%20%20padding%3A%205px%209px%3B%0A%20%20%20%20border%3A%201px%20solid%20red%3B%0A%20%20%20%20color%3A%20red%3B%0A%20%20%20%20border-radius%3A%203px%3B%0A%20%20%7D%0A%0A%20%20.success%7B%0A%20%20%20%20padding%3A%205px%209px%3B%0A%20%20%20%20border%3A%201px%20solid%20green%3B%0A%20%20%20%20color%3A%20green%3B%0A%20%20%20%20border-radius%3A%203px%3B%0A%20%20%7D%0A%0A%20%20form%20span%7B%0A%20%20%20%20color%3A%20red%3B%0A%20%20%7D%0A%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="<style>" title="<style>" />

<div id="respond">
  <?php echo $response; ?>
  <form action="<?php the_permalink(); ?>" method="post">
    <p><label for="name">Name: <span>*</span> <br><input type="text" name="message_name" value="<?php echo esc_attr($_POST['message_name']); ?>"></label></p>
    <p><label for="message_email">Email: <span>*</span> <br><input type="text" name="message_email" value="<?php echo esc_attr($_POST['message_email']); ?>"></label></p>
    <p><label for="message_text">Message: <span>*</span> <br><textarea type="text" name="message_text"><?php echo esc_textarea($_POST['message_text']); ?></textarea></label></p>
    <p><label for="message_human">Human Verification: <span>*</span> <br><input type="text" style="width: 60px;" name="message_human"> + 3 = 5</label></p>
    <input type="hidden" name="submitted" value="1">
    <p><input type="submit"></p>

Excuse the inline style! This is purely for error/success messages, and to put the whole thing in one file. Feel free to put it in your style.css.

There are a number of things going on here. Firstly, we echo the $respond variable, which we’ll look at a bit later.

The rest is just form tags, but you’ll note that the value of them echoes a $_POST variable. This is so that in the case the form is submitted incorrectly, all data is retained so the user doesn’t have to type out everything from scratch.

There’s also a sneaky “hidden” form input, that allows us to check if a user has posted anything or not. I’ll get to that later though.

I’ve added an asterisk next to each input, to signify they are required.

If you save that and refresh, you’ll have a nice-looking contact form!

Building the Form Logic

The logic that follows upon form submission is simple. There are several components that need to be built.

  1. Response generation and messages (based on validation)
  2. Validation
  3. Sending an email

We’ll break it all down below…

Response Generation

To save having messy PHP code, we’ve placed the $response variable within the form. That’s cool, keeping logic mostly separate from views. How do we set this though?

It’s pretty easy really, we need to write a function that fills the $response variable with the feedback we get from validation. This must contain messages to address each validation requirement.

At the top of your page-contact-us.php before get_header(), paste the following.


  //response generation function
  $response = "";

  //function to generate response
  function my_contact_form_generate_response($type, $message){

    global $response;

    if($type == "success") $response = "<div class='success'>{$message}</div>";
    else $response = "<div class='error'>{$message}</div>";


This doesn’t do a whole lot yet, but if you have basic PHP knowledge you’ll understand that we a) create an empty $response variable, and b) fill it with the $message passed depending on its $type, which will either be error or success.

Now when we build the validation, we can call generate_response() at each case (whether it be a success or failure), pass a message (i.e. Email address invalid), and it will be spat out right above the form!

Variables to Get Going

There are 3 groups of variables we need for a successful contact form.

  • Validation Messages
  • Form Input Variables
  • PHP Mailer Variables

The validation process will use all three of these groups to validate, send an email and notify the user of a sent message.

Below the my_contact_form_generate_response() function, write up the following twelve variables:

//response messages
$not_human       = "Human verification incorrect.";
$missing_content = "Please supply all information.";
$email_invalid   = "Email Address Invalid.";
$message_unsent  = "Message was not sent. Try Again.";
$message_sent    = "Thanks! Your message has been sent.";

//user posted variables
$name = $_POST['message_name'];
$email = $_POST['message_email'];
$message = $_POST['message_text'];
$human = $_POST['message_human'];

//php mailer variables
$to = get_option('admin_email');
$subject = "Someone sent a message from ".get_bloginfo('name');
$headers = 'From: '. $email . "\r\n" .
  'Reply-To: ' . $email . "\r\n";

Each group is fairly straight forward. The Response Message group stores all our feedback messages in variables.

The User Posted Variables pick up what has been put into the form, and places them in nicely named variables to save having to access the $_POST variable over and over. $_POST contains all information submitted via a form.

Finally, we set up a couple of variables that are required by PHP’s mail() function, which tell it who to go to, a subject line, and some headers that specify from and reply-to addresses.


This is the meaty bit. We want to check for all possible problems, in the most succinct way possible. This requires several nested if/else conditional statements, but not enough to blow out your brains.

In order, we check for

  1. If the user has submitted the form (using the hidden input)
  2. If not, check the human verification passes (i.e. the user can do math properly!)
  3. If passed, validate the email address
  4. If passed, check for the presence of a name and message (as they’re required)
  5. If all the above passes, send an email!

So let’s get stuck into it!

Human verification

This is pretty simple really. We want to test to see if the form user has entered some math correctly, to ensure they are human. If you look back at our form field, you’ll notice the equation is ? + 3 = 5. So obviously, we want to test first if the value is NOT 2, spit out an error. If it is 2, we then proceed. Below the variable groups, paste the following.

if(!$human == 0){
  if($human != 2) my_contact_form_generate_response("error", $not_human); //not human!
  else {

    //validate email
    //validate presence of name and message
    //send email
else if ($_POST['submitted']) my_contact_form_generate_response("error", $missing_content);

Here, we first check if the human verification is not equal to 0. If it is, then check if it’s incorrect. If so, generate a matching response. The last line checks if the hidden field has been submitted, and if it has, throw another error.

This tests for two cases: empty content and human verification.

If you try to submit the form while it’s empty, you should get a nice error message.

Cool! If you enter anything other than “2” into the human verification, you’ll also get an error.

The next part of validation is email validation. Now we know they’re human, we want to be sure they’re supplying a legit reply address you can contact them with.

To do so very easily, we use a PHP function called filter_var() which has a number of validation models. Obviously, we’ll use the FILTER_VALIDATE_EMAIL model to validate email.

Replace the three lines of comments smack bang in the middle of our validation with the following.

//validate email
if(!filter_var($email, FILTER_VALIDATE_EMAIL))
  my_contact_form_generate_response("error", $email_invalid);
else //email is valid
  //validate presence of name and message
  //send email

Here, we filter the $email variable checking if it’s a valid email address. If not, throw an error. Else, continue!

One line email validation!

Checking for the presence of a name and message is relatively easy. We just use PHP’s empty() function, which returns true or false depending on whether the variable you pass to it is empty.

//validate presence of name and message
if(empty($name) || empty($message)){
  my_contact_form_generate_response("error", $missing_content);
else //ready to go!
  //send email

Finally, the last piece of our puzzle is sending the email. Once all our validation has passed, we use the mail() function PHP has built in to swiftly send an email. We’ve also got to generate a response on success, and on failure as well.

Replace //send email with:

$sent = wp_mail($to, $subject, strip_tags($message), $headers);
if($sent) my_contact_form_generate_response("success", $message_sent); //message sent!
else my_contact_form_generate_response("error", $message_unsent); //message wasn't sent

Believe it or not, you’re done! You’ve now got a fully functional contact form.

If you fill everything out properly, you should get a happy success message that says your email has been sent.


One thing to note is bots are pretty good at getting past simple CAPTCHAS these days, so that is a perk of using a plugin. You can easily implement Google reCAPTCHA into your form. Also, way nicer form validation and form submission using AJAX.

A Contact Form Beyond the Norm

As you can see, you can build your own contact form without resorting to the norm of having one done for you. If anything, it may just end up as a fun project to try. If you like the results — keep it! If not, like everything else, there are alternatives with plugins.

And that’s it. Rolling your own contact page isn’t hard, and it keeps things as fast as possible without any overhead. Depending on your skills, you can, of course, build on top of this. It’s a great way to learn new things as well as build on your experience. Let alone know exactly what happens under the hood!

Extra Credit

If you want to take this further, try getting the form to insert each entry as a custom post type post. You’ll need to use nonces, and wp_insert_post(). You can then even turn it into a completely user-driven testimonials engine!

Have you tried building your own contact form yet? What do you think? Let us know in the comments!
Aileen Javier
Aileen Javier A past writer for WPMU DEV
N. Fakes
N. Fakes Nate is an L.A.-based writer. When he's not pounding keys on his MacBook, you might find him scribbling inappropriate things as a syndicated cartoonist. He's fond of family time, hanging out at Venice Beach, and pizza coupons.