Transactional emails via Resend with React Email templates. All outbound email goes through centralized helpers.
Key files
lib/email.ts- Email sending helpersemails/WelcomeEmail.tsx- New user welcomeemails/MagicLinkSignInEmail.tsx- Magic link sign-inemails/SubscriptionCreatedEmail.tsx- Subscription confirmation
Environment variables
RESEND_API_KEY=re_xxx
EMAIL_FROM_ADDRESS=noreply@yourdomain.com
EMAIL_FROM_NAME=My App
Sending emails
Always use the helpers in lib/email.ts:
import { sendWelcomeEmail } from "@/lib/email";
await sendWelcomeEmail({
user: { email: "user@example.com", name: "John" },
});
Never import Resend directly in pages, routes, or components.
Creating a new template
Use the /add-email-template Cursor command, or manually:
- Create template in
emails/<Name>Email.tsx:
export type WelcomeEmailProps = {
userName: string;
appName: string;
};
export function WelcomeEmail({ userName, appName }: WelcomeEmailProps) {
return (
<Html>
<Body>
<Text>Welcome to {appName}, {userName}!</Text>
</Body>
</Html>
);
}
- Add helper in
lib/email.ts:
export async function sendWelcomeEmail(args: {
user: EmailRecipient;
}): Promise<{ success: boolean }> {
return sendEmail({
to: args.user,
subject: "Welcome!",
react: WelcomeEmail({ userName: args.user.name, appName: siteConfig.name }),
});
}
Event-driven emails
Trigger emails from central flows, not random UI components:
- New user created - Welcome email in
lib/auth.tscreateUser event - Subscription activated - Confirmation in webhook handlers
- Magic link requested - Sign-in link in magic link API route
Error handling
Email functions return { success: boolean } and don't throw. Failures are logged but don't crash the request.
const result = await sendWelcomeEmail({ user });
if (!result.success) {
// Handle failure (optional)
}
Production setup
- Verify your sending domain in Resend
- Update
EMAIL_FROM_ADDRESSto use your domain - Test emails in Resend dashboard