Hugo contact form

Hugo builds the page and the browser posts it, so there is no module to install and nothing for Hugo to do at submit time. The form is a partial, the two values it needs are site params, and a shortcode makes it usable from a content file.

The partial

{{ $formId := .Site.Params.ezform.formid }}
{{ $honeypot := .Site.Params.ezform.honeypot }}

<form action="https://api.ezform.io/f/{{ $formId }}" method="POST">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>

  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <div style="position:absolute;left:-9999px" aria-hidden="true">
    <label for="{{ $honeypot }}">Company website</label>
    <input id="{{ $honeypot }}" name="{{ $honeypot }}" type="text" tabindex="-1" autocomplete="off">
  </div>

  <button type="submit">Send</button>
</form>
[params.ezform]
formid = "YOUR_FORM_ID"
honeypot = "YOUR_HONEYPOT_FIELD"

Call it from a template with {{ partial "ezform-form.html" . }}. The trailing dot matters: without the context, .Site inside the partial is nothing and both values come out empty.

Using it from a content file

Pasting the form’s HTML into a Markdown page looks like it works and does not. Goldmark’s unsafe setting is off by default, so raw HTML in content is dropped from the output, and what you get is a page with no form on it and no error anywhere.

Wrap the partial in a shortcode instead, which is rendered whatever that setting says:

{{ partial "ezform-form.html" . }}
---
title: Contact
---

Tell us what you need and we will get back to you.

{{< contact-form >}}

Allow your origins

Scheme and host, no path, one entry each:

  • http://localhost:1313 for hugo server, if you test against the real endpoint rather than filling the console in by hand.
  • Your production host, which should match baseURL in hugo.toml. The apex and www. are two different origins.
  • Any preview host your deploy setup gives you.

A submission from an origin that is not listed is discarded and never stored, which is what keeps someone who read your form ID out of your inbox. It is also the first thing to check when a form appears to do nothing: see Responses and failure.

What happens when someone submits

The browser posts the form and navigates. Configure a redirect path on the form in the console and ezform answers 303 to that page, so a refresh cannot repost the submission. Without one, ezform renders its own short confirmation page.

A Hugo site ships no JavaScript for this, and that is the point: the form keeps working with JavaScript turned off, which is not true of a form backend that requires a script tag.

Where to go next