Guides · Code

A React contact form that sends to an inbox

One component with fetch: sending, sent and error states, no backend, no SDK and no API key. Works in Vite, Remix and any React app.

  • 5 minutes
  • No backend
  • Free for 250 messages a month

Updated

Step 1 of 60%

Make this guide yours

Type your email and we create your inbox, then put its endpoint in every step. No password, no card.

  1. 1

    The component

    TSX
    import { useState } from "react"
    
    export function ContactForm() {
      const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle")
      async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault()
        setStatus("sending")
        const res = await fetch("https://inboxpanda.io/f/your-form-id", {
          method: "POST",
          headers: { "Content-Type": "application/json", Accept: "application/json" },
          body: JSON.stringify(Object.fromEntries(new FormData(e.currentTarget))),
        })
        setStatus(res.ok ? "sent" : "error")
      }
      if (status === "sent") return <p>Thanks. We'll get back to you soon.</p>
      return (
        <form onSubmit={onSubmit}>
          <label>Name <input name="name" autoComplete="name" required /></label>
          <label>Email <input name="email" type="email" autoComplete="email" required /></label>
          <label>Message <textarea name="message" required /></label>
          <input name="_honey" style={{ display: "none" }} tabIndex={-1} autoComplete="off" />
          <button disabled={status === "sending"}>Send</button>
          {status === "error" && <p>Something went wrong. Please try again.</p>}
        </form>
      )
    }
  2. 2

    How it works

  3. 3

    Show the reason when it fails

    TSX
    const data = await res.json()
    if (!data.ok) setError(data.error) // e.g. "Submission is too large (max 32 KB)."
  4. 4

    Uploads

    TSX
    const res = await fetch("https://inboxpanda.io/f/your-form-id", {
      method: "POST",
      headers: { Accept: "application/json" },
      body: new FormData(e.currentTarget),
    })
  5. 5

    Several forms

    TSX
    <input type="hidden" name="form" value="careers" />
  6. 6

    Check it works

Questions

Is it safe to put the endpoint in client code?

Do I need a form library?

What about Next.js?

Is it free?

Keep going

All guides