Intermediate6 minUpdated Mar 27, 2026

How to Add a Contact Form to Remix

Add a contact form to your Remix app without writing a custom action. FormsList handles submissions so you can skip Remix action loaders entirely.

Prerequisites

  • A Remix project
  • A FormsList account (free)
  • Basic knowledge of React and Remix routing

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.

1

Create a FormsList form endpoint

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_HASH
2

Create the contact route component

Create 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>
  );
}
3

Add the route to your navigation

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>
4

Test and configure notifications

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.

Frequently Asked Questions

Ready to collect form submissions?

Set up your form backend in under a minute. No server required, no complex configuration — just a simple endpoint for your forms.