Skip to main content
Frameworks File 013

React Router

Send email from a React Router (formerly Remix) route action with the top-level mail().

Postboi is an email provider, and its SDK works the same in any framework: read the request’s FormData in a React Router route action and hand it straight to mail(). Postboi extracts the special fields and renders the rest into a tidy HTML table. This is framework mode, which is what Remix became, so a Remix 2 app upgraded to React Router works the same way.

// app/routes/home.tsx
import { Form } from 'react-router'
import { useState } from 'react'
import { mail } from 'postboi'
import type { Route } from './+types/home'

export async function action({ request }: Route.ActionArgs) {
	await mail({ body: request.formData() })
	return { ok: true }
}

export default function Home({ actionData }: Route.ComponentProps) {
	// Mirror the email into the hidden _reply_to field so replies reach the sender.
	const [email, setEmail] = useState('')

	return (
		<Form method="post" encType="multipart/form-data">
			<input type="hidden" name="_subject" value="Contact Form" />
			<input type="hidden" name="_reply_to" value={email} />
			<input name="contact→name" placeholder="Name" required />
			<input
				name="contact→email"
				type="email"
				value={email}
				onChange={(e) => setEmail(e.target.value)}
				required
			/>
			<textarea name="details→message" placeholder="Message" />
			<button type="submit">{actionData?.ok ? 'Sent' : 'Send'}</button>
		</Form>
	)
}

Register the route in app/routes.ts with index('routes/home.tsx'). Field names use the fieldset→field syntax. The provider and default recipient come from postboi.config.ts. A POSTBOI_TOKEN routes to the Postboi provider, or pick any provider.

Or use the Captcha component

postboi/react ships the spam protection (honeypot plus, on the Postboi provider, the managed invisible captcha) as a single prop-free component. It renders inside your existing form, so React Router’s <Form> keeps working:

import { Form } from 'react-router'
import { Captcha } from 'postboi/react'

<Form method="post" encType="multipart/form-data">
	{/* your fields… */}
	<Captcha />
	<button type="submit">Send</button>
</Form>

The publishable key is baked in by bunx postboi sync. See Spam protection.

Runnable example: examples/react-router-provider-postboi.