React

Submit forms from React with controlled state and fetch, or use a plain HTML form for zero-JavaScript submissions.

Controlled form with fetch

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

export function ContactForm() {
  const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus("loading");

    const form = e.currentTarget;
    const data = Object.fromEntries(new FormData(form));

    const res = await fetch("https://formfa.st/f/your_endpoint_id", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify(data),
    });

    if (res.ok) {
      setStatus("success");
      form.reset();
    } else {
      setStatus("error");
    }
  }

  if (status === "success") {
    return <p>Thanks! We'll be in touch.</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="name" placeholder="Name" required />
      <input type="email" name="email" placeholder="Email" required />
      <textarea name="message" placeholder="Message" />

      <input type="hidden" name="_gotcha" style={{ display: "none" }} />

      <button type="submit" disabled={status === "loading"}>
        {status === "loading" ? "Sending..." : "Send"}
      </button>

      {status === "error" && <p>Something went wrong. Please try again.</p>}
    </form>
  );
}

JSON vs FormData

Include the Accept: application/json header to get a JSON response instead of a redirect. This is required for single-page app flows where you want to show a success message inline.

Uncontrolled form (no fetch)

For a simpler approach, use a plain HTML form inside React. The browser handles the submission and redirect:

TSX
export function SimpleForm() {
  return (
    <form action="https://formfa.st/f/your_endpoint_id" method="POST">
      <input type="text" name="name" required />
      <input type="email" name="email" required />
      <textarea name="message" />
      <input type="hidden" name="_gotcha" style={{ display: "none" }} />
      <button type="submit">Send</button>
    </form>
  );
}

With client-side validation

You can combine FormFast with any client-side validation library. Here's an example using native validation attributes:

TSX
<form onSubmit={handleSubmit}>
  <input
    type="text"
    name="name"
    required
    minLength={2}
    maxLength={100}
  />
  <input
    type="email"
    name="email"
    required
  />
  <textarea
    name="message"
    required
    minLength={10}
  />
  <button type="submit">Send</button>
</form>

You can also configure server-side validation rules in the dashboard for an extra layer of protection.