Add a contact form to your Remix app without writing a custom action. FormsList handles submissions so you can skip Remix action loaders entirely.
TL;DR
To add a contact form to Remix without a custom action, submit form data via fetch to `https://formslist.com/f/your-form-id` from a client-side handler. FormsList processes submissions so you don't need a Remix action loader.
Sign up for a free FormsList account and create a new form. Copy the endpoint URL from your dashboard.
// Your endpoint will look like this:
// https://formslist.com/f/YOUR_FORM_HASHCreate a new route file in your Remix app. Instead of using a Remix action, we handle submission client-side with fetch. This keeps the form simple and avoids the need for a server-side action function.
// app/routes/contact.tsx
import { useState } from "react";
import type { MetaFunction } from "@remix-run/node";
export const meta: MetaFunction = () => {
return [
{ title: "Contact Us" },
{ name: "description", content: "Get in touch with us." },
];
};
export default function Contact() {
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus("sending");
const form = e.currentTarget;
const data = new FormData(form);
try {
const res = await fetch("https://formslist.com/f/YOUR_FORM_HASH", {
method: "POST",
body: data,
});
if (res.ok) {
setStatus("sent");
form.reset();
} else {
setStatus("error");
}
} catch {
setStatus("error");
}
}
return (
<main className="mx-auto max-w-2xl py-12 px-4">
<h1 className="text-3xl font-bold mb-6">Contact Us</h1>
<form onSubmit={handleSubmit} className="space-y-4 max-w-md">
<div>
<label htmlFor="name" className="block text-sm font-medium">Name</label>
<input
type="text"
id="name"
name="name"
required
className="mt-1 block w-full rounded border p-2"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium">Email</label>
<input
type="email"
id="email"
name="email"
required
className="mt-1 block w-full rounded border p-2"
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium">Message</label>
<textarea
id="message"
name="message"
rows={4}
required
className="mt-1 block w-full rounded border p-2"
/>
</div>
<button
type="submit"
disabled={status === "sending"}
className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
>
{status === "sending" ? "Sending..." : "Send Message"}
</button>
{status === "sent" && <p className="text-green-600">Message sent successfully!</p>}
{status === "error" && <p className="text-red-600">Something went wrong. Please try again.</p>}
</form>
</main>
);
}Link to the contact page from your site navigation or any other page using Remix's Link component.
import { Link } from "@remix-run/react";
<Link to="/contact">Contact Us</Link>Run `npm run dev`, navigate to /contact, and submit a test message. Verify it appears in your FormsList dashboard. Enable email notifications, auto-responders, or integrations from the dashboard.
Learn how to add a working contact form to your Next.js application in minutes. No backend code required — just create your form, point it at FormsList, and start receiving submissions by email.
Learn moreBuild a fully functional contact form in React with hooks, validation, and email notifications. No backend server or API routes needed.
Learn moreLearn how to process form submissions on any website without writing server-side code. Use a form backend service to receive, store, and forward submissions by email.
Learn moreSet up your form backend in under a minute. No server required, no complex configuration — just a simple endpoint for your forms.