Back to blog
Tutorialstatic sitesAstroHugo

How to Add Forms to Static Sites (Astro, Hugo, Jekyll, 11ty)

6 min read

The static site form problem

Static site generators (Astro, Hugo, Jekyll, 11ty, Gatsby) produce plain HTML files. There's no server to process form submissions. When a user clicks "Submit", the data has nowhere to go.

You have three options:

  • Add a server-side component (defeats the purpose of static)
  • Use a serverless function (AWS Lambda, Vercel Functions, etc.)
  • Use a form backend service

Option 3 is the simplest. Here's how to do it with InputHaven.

The universal approach: plain HTML

This works with every static site generator because it's just HTML:

<form action="https://inputhaven.com/api/v1/submit" method="POST">
  <input type="hidden" name="_form_id" value="your-form-id" />

  <!-- Honeypot spam protection -->
  <div style="position: absolute; left: -9999px;" aria-hidden="true">
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" />
  </div>

  <label for="name">Name</label>
  <input type="text" id="name" name="name" required />

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

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

  <button type="submit">Send</button>
</form>

After submission, the user is redirected to a thank-you page. You can customize this with a _redirect field:

<input type="hidden" name="_redirect" value="https://yoursite.com/thank-you" />

Astro

Astro supports both static HTML and client-side JavaScript. For a static form:

---
// src/pages/contact.astro
import Layout from "../layouts/Layout.astro";
---

<Layout title="Contact">
  <form action="https://inputhaven.com/api/v1/submit" method="POST">
    <input type="hidden" name="_form_id" value="your-form-id" />
    <input type="hidden" name="_redirect" value="/thank-you" />

    <label for="name">Name</label>
    <input type="text" id="name" name="name" required />

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

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

    <button type="submit">Send</button>
  </form>
</Layout>

For a JavaScript-enhanced version with loading states, use an Astro island with React or Vue.

Hugo

In Hugo, create a partial template:

<!-- layouts/partials/contact-form.html -->
<form action="https://inputhaven.com/api/v1/submit" method="POST">
  <input type="hidden" name="_form_id" value="{{ .Site.Params.inputhavenFormId }}" />
  <input type="hidden" name="_redirect" value="{{ .Site.BaseURL }}thank-you/" />

  <div style="position: absolute; left: -9999px;" aria-hidden="true">
    <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" />
  </div>

  <label for="name">Name</label>
  <input type="text" id="name" name="name" required />

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

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

  <button type="submit">Send</button>
</form>

Add your form ID to config.toml:

[params]
  inputhavenFormId = "your-form-id"

Jekyll

In Jekyll, create an include:

<!-- _includes/contact-form.html -->
<form action="https://inputhaven.com/api/v1/submit" method="POST">
  <input type="hidden" name="_form_id" value="{{ site.inputhaven_form_id }}" />
  <input type="hidden" name="_redirect" value="{{ site.url }}/thank-you/" />

  <label for="name">Name</label>
  <input type="text" id="name" name="name" required />

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

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

  <button type="submit">Send</button>
</form>

Eleventy (11ty)

Same HTML approach. In a Nunjucks template:

<!-- src/contact.njk -->
---
layout: base.njk
title: Contact
---

<form action="https://inputhaven.com/api/v1/submit" method="POST">
  <input type="hidden" name="_form_id" value="{{ inputhavenFormId }}" />
  <input type="hidden" name="_redirect" value="/thank-you/" />

  <label for="name">Name</label>
  <input type="text" id="name" name="name" required />

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

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

  <button type="submit">Send</button>
</form>

Key takeaway

The beauty of a form backend is that it works with every static site generator the same way: a standard HTML form with an action URL and a hidden form ID field. No plugins, no build steps, no server-side configuration.

Whether you're using Astro, Hugo, Jekyll, 11ty, Gatsby, or even hand-coded HTML — the integration is identical.

Ready to try InputHaven?

500 free submissions/month. No credit card required.

Get Started Free