The flow
/contact posts through a SvelteKit form action (?/send) with progressive
enhancement — it works without JavaScript and shows inline toasts with it.
The server action in contact/+page.server.ts:
- Honeypot — a
websitefield hidden from humans; bots that fill it get a fake success and their message is dropped - Rate limiting — sliding window, 5 messages per IP per 10 minutes (skipped in dev; swap in Redis for multi-instance production)
- Validation — name/email/subject/message lengths and email shape, returning per-field errors
- Delivery — Resend
emails.send()with escaped HTML + plain text,replyToset to the submitter
Configuration
RESEND_API_KEY="re_..." # required to actually send
CONTACT_FROM_EMAIL="Site <noreply@yourdomain.com>"
CONTACT_TO_EMAIL="inbox@yourdomain.com" Honest dev mode
Without
RESEND_API_KEY the action logs the message to the
server console and the UI says so explicitly — it never pretends email was
delivered.Using email elsewhere
The same pattern generalizes to any transactional email. Resend is imported statically; construct a client inside your action or endpoint:
import { Resend } from 'resend';
const resend = new Resend(env.RESEND_API_KEY);
await resend.emails.send({
from: env.CONTACT_FROM_EMAIL,
to: user.email,
subject: 'Welcome!',
html: '<p>Thanks for signing up.</p>'
}); For welcome/verification flows, prefer Supabase’s built-in auth emails (configurable in the dashboard) and reserve Resend for custom notifications.