React

React contact form that sends email without a backend

React gives you the form, not the part that receives it. Heartdove is a typed form backend with a React hook: your component calls submit, the submission is stored first, the email alert is tracked until it is delivered or bounced, and a retry after a network failure never creates a duplicate.

Updated 20 October 2026

Add the form to React

Install the client from the Heartdove source, create one client with your endpoint and access key, and use the hook inside the component that renders the form. The hook exposes a state with phases idle, submitting, sent and error.

Contact.jsx
import { createClient, readForm } from '@heartdove/forms';
import { useFormSubmit } from '@heartdove/forms/react';

const client = createClient({
  endpoint: 'https://submit.heartdove.com/v1/submit',
  accessKey: 'YOUR_ACCESS_KEY',
});

export function Contact() {
  const { state, submit } = useFormSubmit(client);
  return (
    <form onSubmit={e => { e.preventDefault(); submit(readForm(e.currentTarget).fields); }}>
      <label>Email <input name="email" type="email" required /></label>
      <label>Message <textarea name="message" required /></label>
      <input type="checkbox" name="botcheck" tabIndex={-1} autoComplete="off" style={{ display: 'none' }} />
      <button disabled={state.phase === 'submitting'}>
        {state.phase === 'sent' ? 'Sent' : 'Send message'}
      </button>
    </form>
  );
}

Prefer a plain form? React renders a normal HTML form too. Set the action to the Heartdove endpoint, add the hidden access key, and the browser posts it without any JavaScript.

The @heartdove/forms package ships with the Heartdove source and is not yet published to npm.

Register your website address

Heartdove accepts a submission only from the exact website addresses registered on the form, so a copied key cannot be fired from somewhere else. Register every address the form is published on; each form takes up to 10, and registering a www address covers the bare domain too. For a React site that usually means:

  • https://www.yourdomain.com
  • http://localhost:5173

Testing locally? Register your local address, such as http://localhost:3000, as well.

What happens after submit

The message is saved to your inbox before any email is sent, so a failed alert never loses the enquiry. The alert is then tracked step by step and shown on the message:

  • CapturedSaved and searchable
  • Accepted by providerHanded to the email provider
  • DeliveredThe recipient’s mail server accepted it
  • BouncedShown with the reason; the address is paused

Over your monthly allowance, submissions are held, visibly, and released automatically. Nothing is billed as overage, and each plan’s hold capacity is on the pricing page. The Free plan includes 500 submissions a month with an instant alert for each.

Spam, without a CAPTCHA

Every form gets the exact origin check, a per-visitor rate limit, the hidden trap field and spam scoring with quarantine. Cloudflare Turnstile can be turned on per form for a human check, and is never required. Read how the layers work.

Questions

How do I send email from a React form without exposing credentials?

Do not put SMTP or email API credentials in the browser. Post the form to Heartdove with the public access key; the email is sent from the Heartdove side, and the key alone cannot send email anywhere else.

What does the hook do on a retry?

It keeps the same idempotency key, so a retry after a network failure returns the original receipt instead of storing a second message.

Do I need a CAPTCHA?

No. Every form gets an exact origin check, a per-visitor rate limit, a hidden trap field and spam scoring with quarantine. Cloudflare Turnstile is optional for forms that want a human check.

What happens if I go over the free limit?

Submissions above your monthly allowance are held, visibly, for at least seven days and released automatically when your allowance resets. Each plan’s hold capacity is on the pricing page, and there is no overage charge.

Never lose another enquiry.

Connect your first form in a few minutes. Your existing design stays exactly as it is.