Back to Blog
EnglishApril 19, 2026

React Form Submission Without a Backend (2026)

react form submissionreact contact formform backendreact no backend

Quick summary

Submit React forms without writing a backend. Collect leads, contact requests, and survey responses from any React app using a simple fetch call.

React is a frontend library. It renders UI beautifully — but it has no built-in way to store form submissions, send emails, or connect to a database. You need a backend for that.

The catch: most developers building marketing sites, portfolios, or SaaS landing pages do not want to deploy and maintain a backend just for a contact form. Here is how to handle React form submissions without one.

The Standard (Wrong) Approach

Most tutorials tell you to spin up Express, add a /api/contact endpoint, configure nodemailer, deploy to Railway or Heroku, and manage secrets. That works — but it costs time, money, and ongoing maintenance for something that should take two minutes.

The Simpler Approach: external form endpoint

Flowqen gives you a POST endpoint that receives your form data, stores it, and sends notifications. No backend code needed on your side.

// ContactForm.tsx
import { useState } from "react";

export default function ContactForm() {
  const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");

  async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus("loading");
    try {
      const res = await fetch("https://flowqen.com/api/f/YOUR_FORM_ID", {
        method: "POST",
        body: new FormData(e.currentTarget),
        headers: { Accept: "application/json" },
      });
      if (!res.ok) throw new Error("Submission failed");
      setStatus("success");
    } catch {
      setStatus("error");
    }
  }

  if (status === "success") {
    return <p>Message sent! We'll reply within 24 hours.</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name
        <input name="name" type="text" required />
      </label>
      <label>
        Email
        <input name="email" type="email" required />
      </label>
      <label>
        Message
        <textarea name="message" required />
      </label>

      {/* Honeypot — hides from users, traps bots */}
      <input name="_gotcha" type="text" style={{ display: "none" }} />

      {status === "error" && <p>Something went wrong. Try again.</p>}
      <button type="submit" disabled={status === "loading"}>
        {status === "loading" ? "Sending…" : "Send message"}</button>
    </form>
  );
}

With React Hook Form (Better UX)

If you are already using React Hook Form for validation, you can call Flowqen from the onSubmit handler:

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

type Inputs = { name: string; email: string; message: string };

export default function ContactForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<Inputs>();
  const [sent, setSent] = React.useState(false);

  const onSubmit = async (data: Inputs) => {
    const fd = new FormData();
    Object.entries(data).forEach(([k, v]) => fd.append(k, v));
    await fetch("https://flowqen.com/api/f/YOUR_FORM_ID", {
      method: "POST",
      body: fd,
      headers: { Accept: "application/json" },
    });
    setSent(true);
  };

  if (sent) return <p>Sent!</p>;

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name", { required: true })} placeholder="Name" />
      {errors.name && <span>Required</span>}
      <input {...register("email", { required: true })} type="email" />
      <textarea {...register("message", { required: true })} />
      <button type="submit">Send</button>
    </form>
  );
}

Handling File Uploads

Flowqen supports file uploads out of the box. Add an encType attribute and a file input:

<form
  onSubmit={handleSubmit}
  encType="multipart/form-data"
>
  <input name="attachment" type="file" />
  {/* ...other fields */}
</form>

What Gets Handled For You

  • Submission storage — searchable dashboard with export
  • Email notifications with field-level formatting
  • Spam filtering — honeypot + server-side rate limiting
  • Auto-reply emails to the submitter
  • Slack / Discord / Notion / Google Sheets sync
  • Webhook forwarding to your own endpoints

FAQ

Is it safe to expose the form ID in the frontend?

Yes. The form ID controls only which form receives data, not any credentials. You can restrict allowed domains per form in the dashboard.

Does this work with Vite, Create React App, and other bundlers?

Yes. It is a fetch call — it works in any JavaScript environment.

Can I use JSON instead of FormData?

Yes. Send Content-Type: application/json with a JSON body and Flowqen will parse it correctly.

Or skip all this and use FlowQen — 2 lines of code, done.

Read next

Related guides to help you implement better forms and improve conversions.

Ready to add forms to your website?

Get started with Flowqen for free. No credit card required.

Create your free account