Form Integration

TanStack Contact Form Without a Server Route

TanStack Start and Router already give you file routes. Point the form at a Formbold endpoint with fetch and Accept: application/json, and skip writing your own API route. Free covers 250 submissions a month and 10 forms.

Contact Form for TanStack Start

What does a TanStack Start contact form look like?

Same createFileRoute pattern as the homepage tab, with loading state and a JSON POST. Replace FORM_ID. Works in TanStack Start and any TanStack Router app that can call fetch in the browser.

tanstack.tsx
// src/routes/contact.tsx
import { useState } from "react";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/contact")({
  component: ContactForm,
});

function ContactForm() {
  const [loading, setLoading] = useState(false);
  const [status, setStatus] = useState("");

  async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setLoading(true);

    const formData = new FormData(event.currentTarget);
    const payload = Object.fromEntries(formData.entries());

    try {
      const response = await fetch("https://formbold.com/s/FORM_ID", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: JSON.stringify(payload),
      });
      const result = await response.json();

      if (response.ok) {
        setStatus(result.message || "Message sent.");
        event.currentTarget.reset();
      } else {
        setStatus(result.message || "Something went wrong.");
      }
    } catch {
      setStatus("Could not reach Formbold.");
    } finally {
      setLoading(false);
      setTimeout(() => setStatus(""), 4000);
    }
  }

  return (
    <form onSubmit={onSubmit} className="flex flex-col gap-3 max-w-md">
      <label>
        Email
        <input type="email" name="email" required />
      </label>
      <label>
        Subject
        <input type="text" name="subject" required />
      </label>
      <label>
        Message
        <textarea name="message" rows={5} required />
      </label>
      <button type="submit" disabled={loading}>
        {loading ? "Sending..." : "Send message"}
      </button>
      {status ? <p>{status}</p> : null}
    </form>
  );
}
How it works

How do I wire Formbold into a TanStack route?

One form in the dashboard, one endpoint in the route, one test submit.

1

Create the Formbold form

In All Forms, create a form named after the route, for example Contact. Copy https://formbold.com/s/FORM_ID from Form Setup.

2

Drop it into createFileRoute

Put the component on /contact (or any path). Every input needs a name attribute so FormData keys match what you see in the dashboard.

3

Submit once from the app

Run the TanStack Start or Vite dev server, send a test, then open Submissions. The email arrives on the same event.

Features

Why use Formbold with TanStack instead of a server function?

You already have routing. You still need somewhere to keep entries, send mail and filter spam.

No /api route of your own

The browser talks to Formbold. You do not need a TanStack server function just to accept a contact message.

Email on every plan

Each submission emails you with your field names as labels. Free includes that.

Spam controls in Settings

Turn on Basic Spam Filter, or add Custom reCAPTCHA when bots show up.

File uploads on paid plans

Starter and up accept files at 10 MB, 25 MB or 50 MB depending on the plan.

Slack, Sheets and webhooks

Starter and higher connect Slack, Notion, Google Sheets and webhooks without another worker.

FAQ's

What do TanStack developers ask first?

Common gotchas once the route is live.

Yes for the form itself. Keep the fetch in a client event handler so the POST runs in the browser. Do not put your API token in the route module.