Formpost

Framework guides

Next.js

Server Actions, Route Handlers, and when to use each.

With a Server Action the key never reaches the browser and the form works without client JavaScript. It is the best default for a Next.js site.

app/contact/page.tsx
// Server Action — no client JavaScript required.
import { redirect } from "next/navigation";

export default function ContactPage() {
  async function send(formData: FormData) {
    "use server";

    formData.append("access_key", process.env.CONTACT_ACCESS_KEY!);

    const res = await fetch("https://api.formpost.ai/submit", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: formData,
    });

    // Without this the page just sits there on failure and the visitor
    // assumes it sent. Point these at pages you actually have.
    const { success } = await res.json();
    redirect(success ? "/contact/thanks" : "/contact/error");
  }

  return (
    <form action={send}>
      <input name="name" required />
      <input type="email" name="email" required />
      <textarea name="message" required />
      <button type="submit">Send</button>
    </form>
  );
}

Client-side alternative

If you want inline validation or an optimistic UI, submit from a client component exactly as you would in plain React. Both approaches hit the same endpoint.

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

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

  async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setStatus("sending");

    const data = new FormData(event.currentTarget);
    data.append("access_key", "YOUR_ACCESS_KEY_HERE");

    const res = await fetch("https://api.formpost.ai/submit", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: data,
    });

    const json = await res.json();
    setStatus(json.success ? "sent" : "error");
  }

  return (
    <form onSubmit={onSubmit}>
      <input name="name" required />
      <input type="email" name="email" required />
      <textarea name="message" required />
      <button disabled={status === "sending"}>Send</button>
      {status === "sent" && <p>Thanks — we will be in touch.</p>}
    </form>
  );
}