Skip to main content

Add a contact form to a Hugo site (no backend)

By Maxime Branger · Published on

Why it is tricky with Hugo

Hugo generates static files. There is nothing on the server side to process a submission. So we will create a shortcode that sends the data to AirMess, and you can insert it into any page.

Step 1: declare the URL in the configuration

In hugo.toml :

[params.airmess]
  formUrl = "https://airmess.fr/api/submit/YOUR_TOKEN"

Keeping the URL in the configuration lets you change it without touching the templates.

Step 2: create the shortcode

Create layouts/shortcodes/contact.html :

<form id="contact-form" action="{{ site.Params.airmess.formUrl }}" method="POST">
  <label for="name">Name</label>
  <input id="name" name="name" type="text" required>

  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>

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

  <button type="submit">Send</button>
  <p id="contact-status" role="status"></p>
</form>

<script>
  const form = document.querySelector('#contact-form');
  const status = document.querySelector('#contact-status');

  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    const button = form.querySelector('button');
    button.disabled = true;
    status.textContent = 'Sending…';

    try {
      const res = await fetch(form.action, {
        method: 'POST',
        body: new FormData(form),
      });
      if (!res.ok) throw new Error(String(res.status));
      form.reset();
      status.textContent = 'Thank you, your message has been sent.';
    } catch {
      status.textContent = "Sending failed. Please try again in a moment.";
    } finally {
      button.disabled = false;
    }
  });
</script>

Step 3: use it in a page

In content/contact.md :

---
title: "Contact"
---

A question? Write to me.

{{< contact >}}

Notes

If your theme blocks inline scripts through a Content Security Policy, move the script to static/js/contact.js and load it with a <script src> tag. When testing with hugo server , make sure your local origin is allowed.

The full code of this tutorial is available on GitHub.