integrations · framework

A contact form for React, without building a backend.

A React SPA has nowhere to send a contact form: there's no server behind a Vite build. You don't need to add one. POST the fields to your mailcontact endpoint straight from the browser (CORS is open, so it works from any origin) and messages land in your project's inbox, with a notification in Discord, Slack, email, or a webhook.

The component below is the whole integration: a form, a fetch call, and a useState flag for the success state. No Express server, no serverless function, no email provider SDK. Your build stays a pile of static files.

Prefer zero JavaScript? A plain <form action="…" method="POST"> posts to the same endpoint. See the FAQ for the trade-off.

the snippet

A working form for React, in one paste.

Point the form at your project's mailcontact endpoint. Submissions land in your project inbox. No server, no API route, no email service to wire up.

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

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

  async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    setStatus("sending");
    try {
      const res = await fetch("https://mailcontact.app/api/forms/your-form-id", {
        method: "POST",
        body: data,
      });
      setStatus(res.ok ? "sent" : "error");
    } catch {
      setStatus("error");
    }
  }

  if (status === "sent") {
    return <p>Thanks! Your message is on its way.</p>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="name" placeholder="Your name" />
      <input type="email" name="email" placeholder="you@email.com" required />
      <textarea name="message" placeholder="What's on your mind?" required />
      <button type="submit" disabled={status === "sending"}>
        {status === "sending" ? "Sending…" : "Send"}
      </button>
      {status === "error" && <p>Something went wrong. Please try again.</p>}
    </form>
  );
}

how it works

Three steps, start to finish.

01

Create an inbox

One inbox per project. Name it after your app and you get a form endpoint and a project email address immediately.

02

Drop in the component

Paste ContactForm.tsx, swap in your form id, and render it anywhere: a route, a footer, a modal. The markup is yours to restyle.

03

Get notified

Add a Discord, Slack, email, or webhook channel. Every submission pings you where you already are.

why mailcontact

Built to pair well with React.

No server to bolt onto your SPA

A Vite or CRA build is static files. Because the endpoint accepts cross-origin requests, the browser talks to mailcontact directly. No proxy, no serverless function, nothing extra to deploy.

Success states that feel like React

fetch plus useState keeps visitors inside your app: disable the button while sending, swap in a thank-you message on success. No full-page navigation out of your client-side router.

Your JSX, your design system

It's a plain form component, not an embedded widget or iframe. Style it with Tailwind, CSS Modules, styled-components, whatever the rest of your app uses.

faq

React + mailcontact, answered

Do I really not need a backend or serverless function?

Correct. The endpoint accepts cross-origin POSTs (CORS is open), so the browser submits directly to mailcontact. Your React app can be deployed as pure static files with no functions at all.

How do I show a success message without leaving the page?

Submit with fetch as in the snippet and flip a useState flag when the response comes back ok. Native form posts currently return a JSON response instead of redirecting back to your site (a configurable redirect is coming with the launch), so fetch is the right fit for a SPA today.

Which fields does the endpoint accept, and what are the limits?

Send email and message (both required) plus optional name and subject, as FormData or JSON. Requests are limited to 20 per minute per IP with a 64KB max body, plenty for a contact form.

Can I use react-hook-form or my component library?

Yes. mailcontact only sees the POST, so build the form however you like (react-hook-form, controlled inputs, a UI kit), as long as the request carries the name, email, and message fields.

related

Also works with

One inbox for every React project you ship.

Forms and a real email address, together, with notifications where you live. Launching soon. Be the first to know.

No spam. It's kind of our thing.