Overview
Use Resend for sending emails with React Email templates. The sendEmail utilities handle error handling, logging, and return typed results.
What it is
Resend email service integration with React Email for templating and centralized send utilities.
Why we use it
High deliverability, React-based templates, simple API, and built-in error handling with logging.
When to use
Account verification, password reset, notifications, or any transactional email communication.
Key Features
- React Email templates for type-safe HTML
- Typed result objects (success/error)
- Automatic logging of send attempts
- Integration with Inngest for async sending
Quick Start
Sending Email
Basic email sending with error handling.
// Send an email using Resend
import { sendEmail } from '@/lib/email';
const result = await sendEmail({
to: 'user@example.com',
subject: 'Welcome to Centercode',
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
});
if (result.success) {
console.log('Sent:', result.messageId);
} else {
console.error('Failed:', result.error);
}Patterns
React Email Templates
Creating reusable email templates.
// src/lib/email/templates/welcome.tsx
/* eslint-disable i18next/no-literal-string */
import {
Body,
Container,
Heading,
Link,
Section,
Text,
} from '@react-email/components';
interface WelcomeEmailProps {
userName: string;
loginUrl: string;
}
export function WelcomeEmail({ userName, loginUrl }: WelcomeEmailProps) {
return (
<Body style={{ fontFamily: 'sans-serif' }}>
<Container>
<Section>
<Heading>Welcome, {userName}!</Heading>
<Text>
Thanks for joining Centercode. Click below to get started.
</Text>
<Link
href={loginUrl}
style={{
backgroundColor: '#0066cc',
color: '#ffffff',
padding: '12px 24px',
borderRadius: '4px',
textDecoration: 'none',
}}
>
Log In Now
</Link>
</Section>
</Container>
</Body>
);
}Use the template linting rule to catch common mistakes in email templates.
Send Functions
Dedicated functions for each email type.
// src/lib/email/send.ts
import { Resend } from 'resend';
import { render } from '@react-email/render';
import { logger } from '@/lib/logger';
import { WelcomeEmail } from './templates/welcome';
const resend = new Resend(process.env.RESEND_API_KEY);
const DEFAULT_FROM = 'Centercode <noreply@centercode.com>';
export type SendEmailResult =
| { success: true; messageId: string }
| { success: false; error: string };
export async function sendWelcomeEmail(
to: string,
userName: string,
loginUrl: string
): Promise<SendEmailResult> {
try {
const html = await render(WelcomeEmail({ userName, loginUrl }));
const { data, error } = await resend.emails.send({
from: DEFAULT_FROM,
to,
subject: 'Welcome to Centercode',
html,
});
if (error) {
logger.error({ error, to }, 'Failed to send welcome email');
return { success: false, error: error.message };
}
logger.info({ messageId: data?.id, to }, 'Welcome email sent');
return { success: true, messageId: data!.id };
} catch (error) {
logger.error({ error, to }, 'Exception sending welcome email');
return { success: false, error: String(error) };
}
}Async Email Sending
Using Inngest for reliable delivery.
// Send email via background job for reliability
import { inngest } from '@/lib/jobs';
// In your API route or service
await inngest.send({
name: 'email/send.requested',
data: {
type: 'welcome',
to: user.email,
props: { userName: user.name, loginUrl },
},
});
// In your Inngest function
export const sendEmailJob = inngest.createFunction(
{ id: 'send-email', retries: 3 },
{ event: 'email/send.requested' },
async ({ event, step }) => {
const { type, to, props } = event.data;
await step.run('send-email', async () => {
switch (type) {
case 'welcome':
return await sendWelcomeEmail(to, props.userName, props.loginUrl);
// ... other email types
}
});
}
);Watch Out
Hardcoding email HTML instead of templates
Don't
// Hardcoding email content
await resend.emails.send({
to: email,
subject: 'Your account was created',
html: '<p>Hello! Your account is ready.</p>',
});Do
// Use React Email templates
import { render } from '@react-email/render';
import { WelcomeEmail } from './templates/welcome';
const html = await render(WelcomeEmail({ userName, loginUrl }));
await resend.emails.send({
to: email,
subject: 'Welcome to Centercode',
html,
});- Not handling send failures gracefully
- Sending email synchronously in request handlers
- Missing logging for delivery tracking