FormsFort

Frameworks

FormsFort framework guide for HTML, React, React Hook Form, Next.js, Astro, Vue, Svelte, Angular, and Alpine submission patterns.

Frameworks

Use the same endpoint from any frontend stack. Browser submissions are the default protection model. Server-side submissions are supported when the form has a paid/manual sender IP safelist.

Supported frameworks

Plain HTML

<form action="https://api.formsfort.dev/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
  <input name="name" required />
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

React

See the React guide for detailed examples including React Hook Form, file uploads, and captcha tokens.

async function submitForm(fields) {
  const response = await fetch("https://api.formsfort.dev/submit", {
    method: "POST",
    headers: {
      accept: "application/json",
      "content-type": "application/json",
    },
    body: JSON.stringify({
      access_key: "YOUR_ACCESS_KEY",
      ...fields,
    }),
  });

  return response.json();
}

React Hook Form

Keep the access key in the payload, submit from the browser, and show the API message returned by FormsFort. File fields should use FormData instead of JSON.

import { useState } from "react";
import { useForm } from "react-hook-form";

export function ContactForm() {
  const [result, setResult] = useState("");
  const {
    register,
    handleSubmit,
    reset,
    formState: { errors, isSubmitting },
  } = useForm({ mode: "onTouched" });

  async function onSubmit(fields) {
    setResult("Sending...");

    const response = await fetch("https://api.formsfort.dev/submit", {
      method: "POST",
      headers: {
        accept: "application/json",
        "content-type": "application/json",
      },
      body: JSON.stringify({
        access_key: "YOUR_ACCESS_KEY",
        subject: "New website lead",
        from_name: "Example Site",
        ...fields,
      }),
    });

    const body = await response.json();
    setResult(body.message || (response.ok ? "Submitted." : "Submission failed."));

    if (response.ok) {
      reset();
    }
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("name", { required: "Name is required", maxLength: 80 })}
        autoComplete="name"
      />
      {errors.name && <p>{errors.name.message}</p>}

      <input
        type="email"
        {...register("email", { required: "Email is required" })}
        autoComplete="email"
      />
      {errors.email && <p>{errors.email.message}</p>}

      <textarea {...register("message", { required: "Message is required" })} />
      {errors.message && <p>{errors.message.message}</p>}

      <input type="checkbox" {...register("botcheck")} hidden />
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? "Sending..." : "Send"}
      </button>
      <p role="status">{result}</p>
    </form>
  );
}

Next.js

Next.js server actions and other backend submissions are treated as server-side traffic. They require a paid/manual entitlement and an exact sender IP safelist.

For client-side submissions, use the same React patterns above in a client component.

Astro

Use static HTML forms for content pages, or client scripts when you need inline JSON success and error states.

<form action="https://api.formsfort.dev/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
  <input name="name" required />
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

Vue, Svelte, Angular, Alpine

See the JS Frameworks guide for framework-specific examples.

On this page