MCP Server
Add the Resend MCP server for accurate API guidance:
bunx add-mcp https://resend.com/docs/mcp -ybunx add-mcp https://resend.com/docs/mcp -yThis updates all detected agents automatically. If no agents are detected, add -a opencode -a cursor to the command or prompt the user to specify what agents they want to use for this project.
Step 1: Install the packages
bun add resend @react-email/componentsbun add resend @react-email/componentsThe @react-email/components package is required for Resend to render React email templates.
Step 2: Add environment variables
Add to your .env.development:
RESEND_API_KEY="re_your_api_key"
RESEND_FROM_EMAIL="Your App <noreply@yourdomain.com>"RESEND_API_KEY="re_your_api_key"
RESEND_FROM_EMAIL="Your App <noreply@yourdomain.com>"Then sync to Vercel with bun run env:push.
Get your API key from resend.com/api-keys.
Step 3: Create the resend config
Create the Resend config with email format validation:
import { z } from "zod";
import { loadConfig } from "../common/load-config";
export const resendConfig = loadConfig({
server: {
apiKey: process.env.RESEND_API_KEY,
fromEmail: {
value: process.env.RESEND_FROM_EMAIL,
schema: z
.string()
.regex(
/^.+\s<.+@.+\..+>$/,
'Must match "Name <email@domain.com>" format.',
),
},
},
});import { z } from "zod";
import { loadConfig } from "../common/load-config";
export const resendConfig = loadConfig({
server: {
apiKey: process.env.RESEND_API_KEY,
fromEmail: {
value: process.env.RESEND_FROM_EMAIL,
schema: z
.string()
.regex(
/^.+\s<.+@.+\..+>$/,
'Must match "Name <email@domain.com>" format.',
),
},
},
});Step 4: Create the Resend client
Create the Resend client instance:
import { Resend } from "resend";
import { resendConfig } from "./config";
export const resend = new Resend(resendConfig.server.apiKey);import { Resend } from "resend";
import { resendConfig } from "./config";
export const resend = new Resend(resendConfig.server.apiKey);Step 5: Create the send helper
Create the email sending helper:
import { resend } from "./client";
import { resendConfig } from "./config";
type SendEmailParams = {
to: string | string[];
subject: string;
react: React.ReactElement;
from?: string;
};
export async function sendEmail({ to, subject, react, from }: SendEmailParams) {
const { data, error } = await resend.emails.send({
from: from ?? resendConfig.server.fromEmail,
to: Array.isArray(to) ? to : [to],
subject,
react,
});
if (error) {
throw new Error(`Failed to send email: ${error.message}`);
}
return data;
}import { resend } from "./client";
import { resendConfig } from "./config";
type SendEmailParams = {
to: string | string[];
subject: string;
react: React.ReactElement;
from?: string;
};
export async function sendEmail({ to, subject, react, from }: SendEmailParams) {
const { data, error } = await resend.emails.send({
from: from ?? resendConfig.server.fromEmail,
to: Array.isArray(to) ? to : [to],
subject,
react,
});
if (error) {
throw new Error(`Failed to send email: ${error.message}`);
}
return data;
}Usage
import { sendEmail } from "@/lib/resend/send";
await sendEmail({
to: "user@example.com",
subject: "Welcome!",
react: <WelcomeEmail name="John" />,
});import { sendEmail } from "@/lib/resend/send";
await sendEmail({
to: "user@example.com",
subject: "Welcome!",
react: <WelcomeEmail name="John" />,
});Create email templates
Email templates are React components colocated with the feature that uses them, following the "everything is a library" pattern. Auth-related emails (password reset, email verification) live in src/lib/auth/emails/, while other features would have their own emails/ subfolder.
For example, a password reset email template:
interface ForgotPasswordEmailProps {
resetLink: string;
}
export function ForgotPasswordEmail({ resetLink }: ForgotPasswordEmailProps) {
return (
<div>
<h1>Reset Your Password</h1>
<p>Click the link below to reset your password:</p>
<a href={resetLink}>Reset Password</a>
<p>If you did not request a password reset, please ignore this email.</p>
</div>
);
}interface ForgotPasswordEmailProps {
resetLink: string;
}
export function ForgotPasswordEmail({ resetLink }: ForgotPasswordEmailProps) {
return (
<div>
<h1>Reset Your Password</h1>
<p>Click the link below to reset your password:</p>
<a href={resetLink}>Reset Password</a>
<p>If you did not request a password reset, please ignore this email.</p>
</div>
);
}File Structure
src/lib/resend/
config.ts # Environment validation
client.ts # Resend client instance
send.ts # Email sending helper
src/lib/auth/emails/
forgot-password.tsx # Password reset templatesrc/lib/resend/
config.ts # Environment validation
client.ts # Resend client instance
send.ts # Email sending helper
src/lib/auth/emails/
forgot-password.tsx # Password reset template