How to Add Forms to Static Sites (Astro, Hugo, Jekyll, 11ty)
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.