Sending data

Framework recipes

formto.email is just an HTTP POST endpoint, so any framework works. These are the patterns we recommend.

React

A self-contained controlled form with inline status. Replace YOUR_FORM_ID with your endpoint key.

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

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

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

    const res = await fetch("https://formto.email/f/YOUR_FORM_ID", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(e.currentTarget),
    });

    if (res.ok) {
      e.currentTarget.reset();
      setStatus("ok");
    } else {
      const data = await res.json().catch(() => ({}));
      setError(data.error || "Something went wrong.");
      setStatus("error");
    }
  }

  if (status === "ok") return <p>Thanks — we got your message.</p>;

  return (
    <form onSubmit={onSubmit}>
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button disabled={status === "sending"}>
        {status === "sending" ? "Sending…" : "Send"}
      </button>
      {error && <p role="alert">{error}</p>}
    </form>
  );
}

Next.js (Server Action)

If you want to keep the form ID off the client, post from a Server Action. The endpoint is happy with form-encoded bodies.

app/contact/page.tsxtsx
async function submitContact(formData: FormData) {
  "use server";
  const res = await fetch(`https://formto.email/f/${process.env.FORMTO_ID}`, {
    method: "POST",
    headers: { Accept: "application/json" },
    body: formData,
  });
  if (!res.ok) throw new Error("Submission failed");
}

export default function ContactPage() {
  return (
    <form action={submitContact}>
      <input name="name" required />
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button>Send</button>
    </form>
  );
}

Vue 3

ContactForm.vuevue
<script setup>
import { ref } from "vue";
const status = ref("idle");

async function submit(e) {
  status.value = "sending";
  const res = await fetch("https://formto.email/f/YOUR_FORM_ID", {
    method: "POST",
    headers: { Accept: "application/json" },
    body: new FormData(e.target),
  });
  status.value = res.ok ? "ok" : "error";
  if (res.ok) e.target.reset();
}
</script>

<template>
  <form @submit.prevent="submit" v-if="status !== 'ok'">
    <input name="name" required />
    <input name="email" type="email" required />
    <textarea name="message" required />
    <button>Send</button>
  </form>
  <p v-else>Thanks — we got your message.</p>
</template>

Svelte

ContactForm.sveltesvelte
<script>
  let status = "idle";

  async function submit(e) {
    status = "sending";
    const res = await fetch("https://formto.email/f/YOUR_FORM_ID", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(e.target),
    });
    status = res.ok ? "ok" : "error";
    if (res.ok) e.target.reset();
  }
</script>

{#if status === "ok"}
  <p>Thanks — we got your message.</p>
{:else}
  <form on:submit|preventDefault={submit}>
    <input name="name" required />
    <input name="email" type="email" required />
    <textarea name="message" required />
    <button>Send</button>
  </form>
{/if}

WordPress / Webflow / Squarespace

Any site builder that lets you embed an HTML block works the same as plain HTML — paste a <form> with the right action URL into a code/embed block. You don't need a plugin and you don't need to host anything.

html
<form action="https://formto.email/f/YOUR_FORM_ID" method="POST">
  <input name="name" required />
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <input type="hidden" name="_redirect" value="https://yoursite.com/thanks" />
  <button type="submit">Send</button>
</form>
Don't have a place to put HTML?
Use a hosted form. We give you a public link with a styled form on it — no HTML required.