FormThunder isn't open yet. This site describes the service as it will run at launch. Find out more

FormThunder
Menu

Add a Gatsby contact form without a Gatsby Function

A Gatsby contact form doesn't need a Gatsby Function. Write a normal React component, read the access key from a GATSBY_ prefixed variable, and post to FormThunder from the browser. FormThunder stores each submission and emails you. Your site stays pure static HTML, which means it can be deployed to any host.

Updated

The Gatsby code

.env.production
GATSBY_FORMTHUNDER_KEY=YOUR_ACCESS_KEY
src/components/contact-form.jsx
import * as React from "react";

export default function ContactForm() {
  const [status, setStatus] = React.useState("idle");

  const onSubmit = async (event) => {
    event.preventDefault();
    const form = event.currentTarget;
    setStatus("sending");
    const response = await fetch("https://formthunder.com/submit", {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify({
        ...Object.fromEntries(new FormData(form)),
        access_key: process.env.GATSBY_FORMTHUNDER_KEY,
      }),
    }).catch(() => null);
    const data = response ? await response.json() : { success: false };
    setStatus(data.success ? "sent" : "error");
  };

  return (
    <form onSubmit={onSubmit}>
      <label>Name <input name="name" required /></label>
      <label>Email <input name="email" type="email" required /></label>
      <label>Message <textarea name="message" required /></label>
      <button disabled={status === "sending"}>Send</button>
      {status === "sent" && <p role="status">Thanks, we got your message.</p>}
      {status === "error" && <p role="alert">That didn't send. Please try again.</p>}
    </form>
  );
}
src/pages/contact.jsx
import * as React from "react";
import ContactForm from "../components/contact-form";

export const Head = () => <title>Contact</title>;

export default function ContactPage() {
  return (
    <main>
      <h1>Contact us</h1>
      <ContactForm />
    </main>
  );
}

Replace YOUR_ACCESS_KEY with your free key. It's public by design, so it's safe in client code.

Notes for Gatsby

  • Gatsby only exposes variables starting with GATSBY_ to browser code, and it needs .env.development as well if you test under gatsby develop.
  • This code never touches window during render, so gatsby build server-renders it without errors. fetch only runs inside the handler.
  • If you want a no-JavaScript fallback, add action and method to the form with a hidden access_key, since Gatsby keeps the static HTML.

Or have your AI add it

Connect the MCP server, then ask. The agent creates the form and writes the code for you, and it can send a real test when it's done.

You, to your AI
Add a FormThunder contact form to this Gatsby site that emails hello@example.com. Send a test when you're done.

Questions

Do I need a Gatsby Function?

No. The key is public by design, so the browser can post directly.

Does this work on Gatsby Cloud alternatives like Netlify?

Yes. It's static output, so it works on any host that serves files.

Get your Gatsby form sending

The free plan covers 250 submissions a month, with MCP, the CLI and the brand kit included.