A starterkit contact form

If you want to add a very simple contact form to your website, follow this tutorial to learn how we do it in Kirby's starterkit.

What we'll be doing

We're going to create a simple contact form with Kirby and put it in an easily reusable snippet. We're going to be using Formspree to send the form content straight to your inbox. You'll see, this is super straightforward.


To follow along with your own version of the starterkit, go to the Kirby download page and click on Download now!

Download Kirby Download tutorial files


The form

There's no form included in the starterkit, and no styling either. So we'll have to create that from scratch. Here's what our form will look like.

Step 1 - The blueprint

We're going to create a contact.yml blueprint in the site/blueprints folder.

This contact blueprint will be used by the contact page in the starterkit by default because the content file of the contact page is already called contact.txt.

We're creating those fields:

  • An email field that will be used to send the form content to
  • A label and placeholder field for each input
  • A button submit label
  • A page field to select the success page users will be redirected to


title: Contact
pages: true
files: true
    label: Title
    type:  text
    label: Text
    type:  textarea
    label: Contact Form
    type: headline
    label: Your email address
    type: email
[... download tutorial files for full blueprint ...]

Step 2 - The template

We could create a separate template and copy the default starterkit template here. Or we could learn a bit more about templates and Kirby :)
Kirby tries to load the template of a content page based on the name of the file (i.e. contact.txt or contact.md). If there is no template with the same name, the default template will be used. But there's a bit of magic that lets you know what the intended template was, and use it in your default template.

We're simply going to use the appropriately named intendedTemplate() function to call our contact form snippet straight from the default template, only if the intended template (i.e. the name of the content file) is 'contact'.


<?php snippet('header') ?>
  <main class="main" role="main">
    <div class="text">
      <h1><?php echo $page->title()->html() ?></h1>
      <?php echo $page->text()->kirbytext() ?>
    <?php if($page->intendedTemplate() == 'contact') snippet('contactform'); ?>
<?php snippet('footer') ?>

Step 3 - The contact form snippet

We'll first look at the full snippet code, and then dissect it to explain every bit.


<div id="contact-form">
  <form method="POST"  action="https://formspree.io/<?php echo $page->youremail()?>">      
    <label><?php echo $page->namelabel() ?>  *<input type="text" name="name" placeholder="<?php echo $page->nameplaceholder() ?>" required></label>
    <label><?php echo $page->emaillabel() ?> *<input type="email" name="_replyto" placeholder="<?php echo $page->emailplaceholder() ?>" required></label>
    <label><?php echo $page->messagelabel() ?> *<textarea type="text" rows="3" name="message" placeholder="<?php echo $page->messageplaceholder() ?>" required></textarea></label>
    <input type="text" name="_gotcha"/>
    <input type="hidden" name="_next" value="<?php echo page($page->successpage())->url() ?>" />
    <input type="submit" value="<?php echo $page->submitlabel() ?>">  

Alright, that's quite a lot of code. Let's take it step by step.

<form method="POST"  action="https://formspree.io/<?php echo $page->youremail()?>">

As with most forms on the web, we're wrapping a series of input elements inside a form element. We're using the method="POST" to push the content of our form to the URL provided in the action attribute of the form. In this case, straight to the Formspree server. The way their service works is very easy. Just send the form to them, with your email after the URL, and that's it. That's exactly what we're doing here. We've created a field to contain the email so that it can be changed straight from the panel and we're echoing it.

Next, come our three input elements. Name, email, and message. We'll only look at one in detail as they're all essentially the same.

<label><?php echo $page->namelabel() ?>  *<input type="text" name="name" placeholder="<?php echo $page->nameplaceholder() ?>" required></label>

We're using the label and placeholder we defined earlier in the blueprint. The input name attribute is a mandatory attribute for forsmpree. You can add any number of input elements as long as they have a unique name="x" attribute.
We're using required to make the field mandatory. This is HTML5 validation which works on most modern browsers.

After the three main fields, we have a "honeypot" field:

<input type="text" name="_gotcha"/>

This field is hidden in CSS. If it's not empty on submit, Formspree will ignore the form silently. It's a good way to combat SPAM without too much hassle.

After that, we're adding a hidden input element:

<input type="hidden" name="_next" value="<?php echo page($page->successpage())->url() ?>" />

The _next name is reserved by Formspree to redirect the user on form completion. In our case, we've created a page field in the blueprint and we're now echoing the url of the page selected in the panel. If you download the tutorial files, you'll see we created a hidden page for that purpose. Note: we're offering an alternative solution in the conclusion of this tutorial to avoid creating a new page.

A submit button is all that's left in the form. The snippet is finished!

Step 4 - Adding some styling

It's time to style the form. There's no CSS in the starterkit that we can reuse for the form so we'll have to create our own style. We tried to keep it simple and minimalistic while maintaining the overall starterkit look and feel.

Here are the styles you can add to the end of your stylesheet.


input, textarea{
  border:2px solid #ddd;
  font-size: 1em;
  line-height: 2em;
  font-family: "Source Sans Pro", sans-serif;
  display: block;
  margin-bottom: 1em;
  width: 100%;
  resize: none;
  cursor: pointer;
  background-color: #ddd;
  font-size: 1.25em;
  background-color: white;
  display: none;

Step 5 - Test and confirm the form

Now you can test the form! Forsmpree requires that you confirm your email the first time a form is sent. If you submit the form for the first time, you'll be greeted with a message that asks you to confirm your form (see image on the right). Go check your inbox and click on the confirmation button. The next time you fill in the form, there won't be any Formspree messages and you'll be sent right away to your confirmation page.

An alternative return page

This alternative comes from a suggestion by Ian Cox. He didn't want to create a new page on a website just for the thank you page, so instead, he wanted to return to the same page and pass a parameter in the URL. This modified snippet does the trick:


<div id="contact-form">
  <?php if(param('contact')) :?>
    This is a thank you message!
  <?php else: ?>
  <form method="POST"  action="https://formspree.io/<?php echo $page->youremail()?>">      
    <label><?php echo $page->namelabel() ?>  *<input type="text" name="name" placeholder="<?php echo $page->nameplaceholder() ?>" required></label>
    <label><?php echo $page->emaillabel() ?> *<input type="email" name="_replyto" placeholder="<?php echo $page->emailplaceholder() ?>" required></label>
    <label><?php echo $page->messagelabel() ?> *<textarea type="text" rows="3" name="message" placeholder="<?php echo $page->messageplaceholder() ?>" required></textarea></label>
    <input type="text" name="_gotcha"/>
    <input type="hidden" name="_next" value="<?php echo url(page() . '/' . url::paramsToString(['contact' => 'true'])) ?>" />
    <input type="submit" value="<?php echo $page->submitlabel() ?>">  
  <?php endif ?>

Before displaying the form, we're testing to see if the url contains the parameter contact with the value true.
You'll see if you look at the next hidden input that we've changed the destination URL. We're now echoing the URL of the current page and adding a parameter contact = true. So that basically on a successful submit, users will be redirected to the same URL with an additional parameter at the end of the URL.
We've now wrapped the form to be only displayed if that contact parameter is false. The form will not display on a successful submit!
If you want to test this in the tutorial files, replace the call to the snippet in the default.php template from contactform to contactform2.

Going further

If you want to improve upon this form, here are a couple ideas you could consider:

  1. Using AJAX (with a fallback when Javascript doesn't exist) to submit the form. Formspree supports AJAX by default and provides an example in their documentation.
  2. Using a controller to send the form so that you can hide your email address from the form and potentially add information you don't want users to see in your page.

Stay tuned!

Want to always know the latest Maco news? Subscribe and receive the newsletter straight in your inbox. No spam guarantee!

Write a comment