Frameworks and libraries
Add a contact form to Remix
Remix (now React Router v7) is built around the platform's own form semantics, so a contact form is already progressive by default. All that is missing is the destination. Forward the FormData from your action to a WebForms endpoint and you get storage, spam filtering and fan-out without adding a mail provider to your stack.
Setup
Copy your endpoint
Create a form in the dashboard.
Forward from your action
Your action already receives FormData. Pass it straight through.
Return the result
Return validation issues to the component so useActionData can render them inline.
Remix / React Router examples
Swap in your own endpoint and these run as-is.
import { Form, useActionData, useNavigation } from "react-router";
import type { Route } from "./+types/contact";
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const res = await fetch("https://send.webforms.to/wft_your_key", {
method: "POST",
headers: { accept: "application/json" },
body: formData,
});
const data = await res.json();
if (!data.ok) return { error: data.message ?? "Submission failed" };
return { ok: true };
}
export default function Contact() {
const result = useActionData<typeof action>();
const nav = useNavigation();
const sending = nav.state === "submitting";
if (result?.ok) return <p role="status">Thanks, we got it.</p>;
return (
<Form method="post">
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<button disabled={sending}>{sending ? "Sending…" : "Send"}</button>
{result?.error && <p role="alert">{result.error}</p>}
</Form>
);
}Worth knowing
- Because the action runs on your server, submissions arrive from your server's IP. If per-visitor spam signals matter more than server-side control, post from the client instead.
Remix / React Router form FAQ
Yes, and it is the better fit when you want to validate server-side or redirect afterwards. The action receives the FormData, forwards it with fetch, and returns a redirect or a validation error. Posting directly is simpler when the form is purely a contact form.
Yes. A plain form with action and method works before hydration; useFetcher upgrades it to a background submit afterwards. Nothing about the endpoint requires JavaScript, so a visitor with a slow connection still gets a working form.
Yes. That is the point of Remix's <Form>. The action runs on a normal browser POST and the response is a normal navigation.
Related guides
Where Remix / React Router submissions can go
The guide above gets the submission to your endpoint. From there it can land anywhere: one destination on Free, several on Pro.
Need the form markup too? The template gallery has complete forms for contact, waitlist, job application and more, and the form generator builds one field by field.
Ready to wire up your Remix / React Router form?
Free for 300 submissions a month. Endpoint in under a minute, no card required.
Start free