Back to Blog
EnglishApril 19, 2026

How to Add a Form to Hugo, Jekyll, or Gatsby (No Backend)

hugo contact formjekyll contact formgatsby formstatic site form

Quick summary

Add a working contact form to your Hugo, Jekyll, or Gatsby site in minutes. No server required. Works out of the box with any static site generator.

Hugo, Jekyll, and Gatsby are all excellent static site generators. They compile to pure HTML, CSS, and JavaScript — which means they cannot handle form submissions on their own. You need an external form endpoint.

This guide shows how to add a working contact form to each of these generators using Flowqen as the backend. The setup takes under 5 minutes.

How It Works

When a user submits your form, the browser sends a POST request directly to Flowqen's servers. Flowqen stores the submission, sends you a notification, and (optionally) redirects the user back to your site. Your static site generator is never involved in the submission flow.

Hugo — Contact Form

In Hugo, create a partial template at layouts/partials/contact-form.html:

<form
  action="https://flowqen.com/api/f/YOUR_FORM_ID"
  method="POST"
>
  <input type="text" name="name" placeholder="Your name" required />
  <input type="email" name="email" placeholder="Email address" required />
  <textarea name="message" placeholder="Your message" rows="5" required></textarea>
  <input type="hidden" name="_redirect" value="{{ .Site.BaseURL }}thanks/" />
  <!-- Honeypot spam trap -->
  <input type="text" name="_gotcha" style="display:none" />
  <button type="submit">Send message</button>
</form>

Include the partial in any page template:

{{ partial "contact-form.html" . }}

Create a content/thanks.md page so users land somewhere after submission:

---
title: "Message Sent"
---
Thanks! We'll reply within 24 hours.

Jekyll — Contact Form

Create an _includes/contact-form.html file:

<form
  action="https://flowqen.com/api/f/YOUR_FORM_ID"
  method="POST"
>
  <input type="text" name="name" placeholder="Name" required />
  <input type="email" name="email" placeholder="Email" required />
  <textarea name="message" placeholder="Message" required></textarea>
  <input type="hidden" name="_redirect" value="{{ site.url }}/thanks" />
  <input type="text" name="_gotcha" style="display:none" />
  <button type="submit">Send</button>
</form>

Include it in your layout:

{% include contact-form.html %}

Add a thanks.html page at the root:

---
layout: default
title: Thanks
---
<h1>Message received!</h1>
<p>We'll get back to you soon.</p>

Gatsby — Contact Form

Create a React component at src/components/ContactForm.tsx:

import React, { useState } from "react";

export default function ContactForm() {
  const [submitted, setSubmitted] = useState(false);

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const data = new FormData(e.currentTarget);
    await fetch("https://flowqen.com/api/f/YOUR_FORM_ID", {
      method: "POST",
      body: data,
      headers: { Accept: "application/json" },
    });
    setSubmitted(true);
  }

  if (submitted) {
    return <p>Thanks! We'll be in touch soon.</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" type="text" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" required />
      <input name="_gotcha" type="text" style={{ display: "none" }} />
      <button type="submit">Send message</button>
    </form>
  );
}

Import and render in any Gatsby page:

import ContactForm from "../components/ContactForm";
export default function ContactPage() {
  return <main><ContactForm /></main>;
}

Eleventy (11ty) — Contact Form

Same approach as Hugo/Jekyll — create an include file and point the form action at your Flowqen endpoint:

<form action="https://flowqen.com/api/f/YOUR_FORM_ID" method="POST">
  <input name="name" required />
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button>Send</button>
</form>

Spam Protection

All examples include a honeypot field (name="_gotcha"). Bots fill it in; real users leave it blank. Flowqen filters any submission where this field has a value. For higher-traffic forms, enable Cloudflare Turnstile from the dashboard — no user friction.

After Submission: What Happens

  • You receive an email notification with all field values.
  • The submission appears in your Flowqen dashboard.
  • Optional: route to Slack, Google Sheets, Notion, Discord, webhook, or your CRM.

FAQ

Can I use this with GitHub Pages?

Yes. GitHub Pages hosts static files — the form POSTs to Flowqen, not to GitHub. Works perfectly.

Does this work with Cloudflare Pages?

Yes. Same reasoning — the form backend is external.

What if I need custom validation before submission?

Add JavaScript validation client-side before calling fetch. Flowqen also validates required fields server-side.

Or skip all this and use FlowQen — 2 lines of code, done.

Read next

Related guides to help you implement better forms and improve conversions.

Ready to add forms to your website?

Get started with Flowqen for free. No credit card required.

Create your free account