Skip to content
FFormhook
Back to blog

Blog

How to add a contact form to GitHub Pages

· 3 min read

GitHub Pages serves static files only - no PHP, no server code, no way to receive a form POST. So a contact form needs an external endpoint to send to. Here are the three real options, and the one-minute version.

Option 1: a form backend (recommended)

Point your form's action at a hosted form endpoint:

<form action="https://formhook.app/f/fh_your-key" method="POST">
  <label>Email <input type="email" name="email" required></label>
  <label>Message <textarea name="message" required></textarea></label>
  <button type="submit">Send</button>
</form>

Commit, push, done. Submissions land in a dashboard and ping your phone via push notification. Spam is filtered by a honeypot and rate limits before it ever reaches you. This works identically for plain HTML repos and Jekyll sites (GitHub Pages' native generator - see the Jekyll guide for a themed include, or the plain HTML guide).

Pros: one attribute, no JavaScript required, spam handled, submissions stored.
Cons: it's a third-party service - so check the free-tier limits and retention policy of whichever you pick. Formhook's free tier keeps submissions forever.

Option 2: a mailto: link

<a href="mailto:you@example.com">Email me</a>

Honest but weak: it depends on the visitor having a configured mail client (increasingly untrue on desktop), exposes your address to scrapers, gives you no spam protection, and tells you nothing about drop-off. Fine for a hobby README page; costly for anything where enquiries are money. (More on that trade-off in mailto vs contact form.)

Option 3: a serverless function elsewhere

You can keep the site on GitHub Pages and receive the POST with a Cloudflare Worker or similar. It works - but now you're writing and maintaining the receiver: validation, spam filtering, storage, email delivery, and CORS. You've reintroduced exactly the backend GitHub Pages let you avoid. Reasonable if you enjoy it; unnecessary if you don't.

The AJAX upgrade (optional)

The plain HTML form navigates away on submit. To stay on the page:

<script>
document.querySelector("form").addEventListener("submit", async (e) => {
  e.preventDefault();
  const res = await fetch(e.target.action, {
    method: "POST",
    body: new FormData(e.target),
    headers: { Accept: "application/json" },
  });
  e.target.outerHTML = res.ok
    ? "<p>Thanks - message sent.</p>"
    : "<p>Something went wrong. Please email us directly.</p>";
});
</script>

No build step, no dependencies - it works in a raw GitHub Pages repo.

Custom domain note

If your Pages site runs on a custom domain, make sure your form service's per-form settings (like a CORS allowlist) include that domain, not just username.github.io. In Formhook that's one field in the form's settings.

That's the whole story: GitHub Pages handles your site, a form endpoint handles the one thing it can't. Start free and the form is live before your next git push finishes.

Ship a working form in one line

EU-hosted, submissions kept forever, push notifications on every tier.

Start free

No credit card · read the docs

Keep reading