Skip to content

React (@frontmail/react)

Terminal window
npm install @frontmail/react

Works with React 18 and 19, including Next.js (use it in client components).

Wrap your app (or just the part with forms) in FrontmailProvider. Keep the options object stable – define it at module level or with useMemo:

import { FrontmailProvider } from '@frontmail/react';
const frontmailOptions = { publicKey: 'pk_4f2a…', blockHeadless: true };
export function App() {
return (
<FrontmailProvider options={frontmailOptions}>
<Routes />
</FrontmailProvider>
);
}

options accepts everything init() does. Alternatively, pass an existing client: <FrontmailProvider client={client}>.

The fastest way to a working form – it submits via sendForm, renders Turnstile when you pass a site key and exposes the send state:

import { FrontmailForm } from '@frontmail/react';
export function ContactForm() {
return (
<FrontmailForm
serviceId="svc_01J9…"
templateId="tpl_contact"
turnstileSiteKey="YOUR_TURNSTILE_SITE_KEY"
onSuccess={(result) => console.log(result.status)}
onError={(error) => console.error(error.code)}
>
{({ status, error }) => (
<>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<button disabled={status === 'sending'}>Send</button>
{status === 'sent' && <p>Thank you!</p>}
{status === 'held' && <p>Received – it will be delivered shortly.</p>}
{error && <p role="alert">{error.message}</p>}
</>
)}
</FrontmailForm>
);
}

Props: serviceId, templateId, turnstileSiteKey, turnstileOptions, sendOptions, resetOnSuccess (default true), onSuccess, onError, plus any <form> attribute. Children can be nodes or a function of the state { status, error, result }.

turnstileSiteKey is the site key of your own Turnstile widget (Security → Bot protection (Turnstile) in the dashboard) – see Cloudflare Turnstile. Leave it out if the template doesn’t require a CAPTCHA.

For custom UIs and controlled forms:

import { useState } from 'react';
import { useSendEmail } from '@frontmail/react';
function Newsletter() {
const { send, status, error, reset } = useSendEmail('svc_01J9…', 'tpl_newsletter');
const [email, setEmail] = useState('');
return (
<form onSubmit={async (e) => { e.preventDefault(); await send({ email }); }}>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
<button disabled={status === 'sending'}>Subscribe</button>
{status === 'error' && <p role="alert">{error?.message}</p>}
</form>
);
}

status is idle → sending → sent | held | error. send() and sendForm() resolve with the result or undefined on error (the error is in error), so you don’t need try/catch.

Returns the client from the provider for anything else, e.g. getStatus:

import { useFrontmail } from '@frontmail/react';
const client = useFrontmail();
const status = await client.getStatus(messageId, { token });

With generated types, useSendEmail('svc_…', 'tpl_contact').send({...}) type-checks the params of that template.

Example project: examples/react.