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

FormThunder
Menu

Add a React contact form that emails you without a backend

A React contact form doesn't need a server of its own. This component reads its fields with FormData, posts JSON to FormThunder and tracks sending, sent and error states in useState. Submissions are stored and emailed to your verified address. Your AI can drop it in for you: get_snippet returns this code with your key filled in.

Updated

The React code

ContactForm.jsx
import { useState } from "react";

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

  async function handleSubmit(event) {
    event.preventDefault();
    const form = event.currentTarget;
    setStatus("sending");
    try {
      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: "YOUR_ACCESS_KEY",
          subject: "New message from the website",
        }),
      });
      const data = await response.json();
      setStatus(data.success ? "sent" : "error");
      if (data.success) form.reset();
    } catch {
      setStatus("error");
    }
  }

  if (status === "sent") return <p role="status">Thanks, we got your message.</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input type="checkbox" name="botcheck" style={{ display: "none" }} tabIndex={-1} />
      <label htmlFor="name">Name</label>
      <input id="name" name="name" required />
      <label htmlFor="email">Email</label>
      <input id="email" name="email" type="email" required />
      <label htmlFor="message">Message</label>
      <textarea id="message" name="message" rows={5} required />
      <button disabled={status === "sending"}>
        {status === "sending" ? "Sending..." : "Send"}
      </button>
      {status === "error" && <p role="alert">That didn't send. Please try again.</p>}
    </form>
  );
}

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

Notes for React

  • Keep form in a variable before the first await. React clears event.currentTarget once the handler has yielded.
  • Uncontrolled inputs are enough here. You don't need a useState per field unless you validate as the visitor types.
  • Read the key from your build tool's env if you prefer, such as import.meta.env.VITE_FORMTHUNDER_KEY. It's public either way, so this is about tidiness.

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 React site that emails hello@example.com. Send a test when you're done.

Questions

Do I need React Hook Form or Formik?

Not for this. They're useful for long forms with live validation, and you can call the same fetch from their submit handlers.

Does this work with React 19 form actions?

Yes. Use the same fetch inside your action function, and read the fields from the FormData it receives.

Get your React form sending

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