Back to blog

    React Contact Form in Gatsby (No Backend Required)

    Add a working contact form to your Gatsby site using React hooks and fetch. Submissions go to Formboost — no serverless functions, no backend, no spam.

    React Contact Form in Gatsby (No Backend Required)

    Gatsby builds static sites, which means there's no server to receive form submissions. Most tutorials suggest Netlify Forms or writing a serverless function — but you can skip both. This guide shows how to add a contact form to any Gatsby site that just works, using React hooks and Formboost.

    Why Not Netlify Forms?

    Netlify Forms only work when your site is deployed on Netlify. Formboost works everywhere — Netlify, Vercel, GitHub Pages, S3, your own server, anywhere. And it gives you a proper submission dashboard, AI spam filtering, and webhooks — things Netlify Forms don't include out of the box.

    The Contact Form Component

    Create src/components/ContactForm.js (or .tsx):

    1import { useState } from "react"
    2
    3export default function ContactForm() {
    4  const [status, setStatus] = useState("idle") // idle | sending | sent | error
    5
    6  async function handleSubmit(e) {
    7    e.preventDefault()
    8    setStatus("sending")
    9
    10    const data = Object.fromEntries(new FormData(e.target))
    11
    12    try {
    13      const res = await fetch("https://formboost.app/f/YOUR_ENDPOINT_ID", {
    14        method: "POST",
    15        headers: { "Content-Type": "application/json" },
    16        body: JSON.stringify(data),
    17      })
    18
    19      if (!res.ok) throw new Error()
    20      setStatus("sent")
    21    } catch {
    22      setStatus("error")
    23    }
    24  }
    25
    26  if (status === "sent") {
    27    return <p>Thanks! We'll get back to you soon.</p>
    28  }
    29
    30  return (
    31    <form onSubmit={handleSubmit}>
    32      <div>
    33        <label htmlFor="name">Name</label>
    34        <input id="name" type="text" name="name" required />
    35      </div>
    36
    37      <div>
    38        <label htmlFor="email">Email</label>
    39        <input id="email" type="email" name="email" required />
    40      </div>
    41
    42      <div>
    43        <label htmlFor="message">Message</label>
    44        <textarea id="message" name="message" rows={5} />
    45      </div>
    46
    47      {status === "error" && (
    48        <p style={{ color: "red" }}>Something went wrong. Please try again.</p>
    49      )}
    50
    51      <button type="submit" disabled={status === "sending"}>
    52        {status === "sending" ? "Sending…" : "Send Message"}
    53      </button>
    54    </form>
    55  )
    56}

    Replace YOUR_ENDPOINT_ID with your Formboost endpoint ID.

    Using It in a Page

    1// src/pages/contact.js
    2import React from "gatsby"
    3import ContactForm from "../components/ContactForm"
    4
    5export default function ContactPage() {
    6  return (
    7    <main>
    8      <h1>Contact Us</h1>
    9      <ContactForm />
    10    </main>
    11  )
    12}
    13
    14export const Head = () => <title>Contact | My Site</title>

    TypeScript Version

    1// src/components/ContactForm.tsx
    2import { useState } from "react"
    3
    4type Status = "idle" | "sending" | "sent" | "error"
    5
    6export default function ContactForm() {
    7  const [status, setStatus] = useState<Status>("idle")
    8
    9  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    10    e.preventDefault()
    11    setStatus("sending")
    12
    13    const data = Object.fromEntries(new FormData(e.currentTarget))
    14
    15    try {
    16      const res = await fetch("https://formboost.app/f/YOUR_ENDPOINT_ID", {
    17        method: "POST",
    18        headers: { "Content-Type": "application/json" },
    19        body: JSON.stringify(data),
    20      })
    21
    22      if (!res.ok) throw new Error()
    23      setStatus("sent")
    24    } catch {
    25      setStatus("error")
    26    }
    27  }
    28
    29  if (status === "sent") return <p>Thanks! We'll be in touch.</p>
    30
    31  return (
    32    <form onSubmit={handleSubmit}>
    33      <input name="name" type="text" required placeholder="Name" />
    34      <input name="email" type="email" required placeholder="Email" />
    35      <textarea name="message" rows={5} placeholder="Message" />
    36      {status === "error" && <p>Something went wrong.</p>}
    37      <button type="submit" disabled={status === "sending"}>
    38        {status === "sending" ? "Sending…" : "Send"}
    39      </button>
    40    </form>
    41  )
    42}

    Adding Validation with react-hook-form

    For more control over validation and error messages:

    1npm install react-hook-form
    1import { useForm } from "react-hook-form"
    2
    3type FormValues = { name: string; email: string; message: string }
    4
    5export default function ContactForm() {
    6  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormValues>()
    7
    8  async function onSubmit(data: FormValues) {
    9    const res = await fetch("https://formboost.app/f/YOUR_ENDPOINT_ID", {
    10      method: "POST",
    11      headers: { "Content-Type": "application/json" },
    12      body: JSON.stringify(data),
    13    })
    14    if (!res.ok) throw new Error("Submission failed")
    15  }
    16
    17  return (
    18    <form onSubmit={handleSubmit(onSubmit)}>
    19      <input {...register("name", { required: "Name is required" })} />
    20      {errors.name && <span>{errors.name.message}</span>}
    21
    22      <input type="email" {...register("email", { required: "Email is required" })} />
    23      {errors.email && <span>{errors.email.message}</span>}
    24
    25      <textarea {...register("message")} rows={5} />
    26
    27      <button type="submit" disabled={isSubmitting}>
    28        {isSubmitting ? "Sending…" : "Send"}
    29      </button>
    30    </form>
    31  )
    32}

    Getting Your Endpoint

    1. Sign up at dashboard.formboost.app — free, no credit card
    2. Create a new endpoint and copy the URL
    3. Replace YOUR_ENDPOINT_ID in the examples above

    Start collecting Gatsby form submissions free →