How to Send Emails
Deep dive into using Resend + React Email for sending emails and managing newsletters in NextDevKit, mastering the complete email system functionality.
In the previous Auth tutorial, we've encountered email functionality like sending verification emails and password reset emails. Now let's start from the basics, learn React Email core concepts, and master NextDevKit's complete email system.
📦 Three Email Categories
Before diving into the technical details of implementing email services, let's first understand what types of emails traditional SaaS services need and how emails are categorized.
Most traditional SaaS services categorize emails into three types, each with different purposes and best practices:
1. Operational Email
- System-triggered, users must receive
- Examples: Email verification, password reset, login notifications
- Characteristics: Time-sensitive, concise content, clear action buttons
2. Transactional Email
- Triggered by user actions, contains transaction information
- Examples: Order confirmation, payment receipts, contact form confirmations
- Characteristics: Detailed information, standardized format, needs archiving
3. Marketing Email
- Users can choose to receive, marketing-oriented
- Examples: Welcome emails, newsletters, product updates
- Characteristics: Beautiful design, rich content, includes unsubscribe option
NextDevKit only has built-in templates for Operational Email by default. Templates for Transactional Email and Marketing Email are not implemented by default, because I still recommend implementing them based on your specific business needs. Different businesses indeed have different designs in this regard.
🤔 Why React Email?
After understanding what email services we need, let's see why we need React Email to develop email templates.
Traditional HTML Email Pain Points
Traditional email development faces numerous challenges:
1. Compatibility Nightmare
- Different email clients (Outlook, Gmail, Apple Mail) have vastly different CSS support
- Must use outdated HTML table layouts
- Responsive design is difficult to implement
2. Low Development Efficiency
- Pure HTML templates are hard to maintain and reuse
- Dynamic content insertion is error-prone
- Lack of type safety and development tool support
3. Difficult Testing and Preview
- Need manual testing across multiple email clients
- Debugging styling issues is time-consuming
React Email's Solutions
React Email solves these problems through:
✅ Component-based Development: Use familiar React syntax to build email templates
✅ Cross-client Compatibility: Auto-generate HTML compatible with major email clients
✅ TypeScript Support: Complete type safety and IntelliSense
✅ Real-time Preview: Built-in development server for real-time email preview
✅ Responsive Design: Built-in responsive components, auto-adapt to mobile
// ❌ Traditional HTML email
`<table style="width:100%;">
<tr>
<td style="padding:20px;">
<h1 style="color:#333;">Welcome ${userName}!</h1>
<a href="${link}" style="background:#007bff;color:white;">Get Started</a>
</td>
</tr>
</table>`
// ✅ React Email components
<Container style={{padding: '20px'}}>
<Heading>Welcome {userName}!</Heading>
<Button href={link}>Get Started</Button>
</Container>
🎨 Tailwind CSS + React Email
NextDevKit itself integrates Tailwind CSS as the default built-in CSS framework for developing component styles, so using Tailwind CSS to develop email templates provides a more unified development experience.
NextDevKit adopts the modern Tailwind CSS + React Email approach, bringing unprecedented development experience.
Why Choose Tailwind CSS?
Traditional email development requires writing extensive inline styles, making maintenance difficult. Tailwind CSS solves this with predefined utility classes:
// ❌ Traditional inline styles
<Text style={{
fontSize: "18px",
fontWeight: "600",
marginBottom: "16px",
color: "#6b7280"
}}>
// ✅ Tailwind CSS classes
<Text className="text-lg font-semibold mb-4 text-muted-foreground">
Key Advantages
1. Development Efficiency: Predefined class names for quick styling
2. Consistency: Built-in design system prevents style inconsistencies
3. Responsive: Built-in responsive modifiers
4. Maintainability: Semantic class names, easy to understand and modify
React Email Core Components
React Email provides email-specific components ensuring cross-client compatibility:
Layout Components: Html
, Head
, Body
, Container
, Section
Text Components: Heading
, Text
, Link
Interactive Components: Button
Media Components: Img
Helper Components: Hr
, Row
, Column
Key Features:
- ✅ Auto-generate table layouts for compatibility
- ✅ Built-in responsive support
- ✅ TypeScript type safety
- ✅ Support for Tailwind CSS classes
Practical Usage Example
The following example is not a NextDevKit built-in email template, it's recommended to design email templates based on your business needs.
import { Container, Text, Button, Hr } from "@react-email/components";
<Container className="bg-white p-6 rounded-lg">
<Text className="text-2xl font-bold text-center mb-4">
Welcome to join us!
</Text>
<Text className="text-base text-gray-600 mb-6">
Thank you for registering, let's start your journey.
</Text>
<Hr className="my-6" />
<div className="text-center">
<Button
href="#"
className="bg-blue-600 text-white px-6 py-3 rounded-md font-semibold"
>
Get Started →
</Button>
</div>
</Container>
Key Points:
- Use Tailwind class names instead of inline styles
- Responsive classes automatically handle mobile adaptation
- Semantic class names improve code readability
🏗️ NextDevKit Email System Architecture
Now let's understand how NextDevKit integrates React Email into real projects:
System Design Principles
NextDevKit's email system is designed based on these principles:
1. Separation of Concerns: Templates, sending logic, and providers are separated
2. Type Safety: Complete TypeScript support
3. Internationalization: Built-in multi-language support
4. Extensibility: Support for multiple email service providers and more email templates
Architecture Components
Core Module Functions:
- templates/: React Email template components
- components/: Reusable email components
- providers/: Email service provider abstraction layer
- actions.ts: Newsletter-related Server Actions
- index.ts: Core sending function
sendEmail
⚙️ Environment Configuration
Resend Setup Guide
Step 1: Create Resend Account
Visit Resend official website to register and verify your sending domain.
Step 2: Get API Key
In Resend Dashboard → API Keys → Create new API Key (ensure send permissions are checked).
Step 3: Configure Environment Variables
# Required - for sending emails
RESEND_API_KEY="re_your_api_key_here"
# Optional - only needed for Newsletter functionality
RESEND_AUDIENCE_ID="aud_your_audience_id_here"
Step 4: Application Configuration
export const appConfig = {
mail: {
provider: "resend",
from: "noreply@yourcompany.com", // Your verified domain
contact: "contact@yourcompany.com", // Contact email
},
} as const;
Key Tips:
- Production environment must use verified domains
- Development can use Resend's test domain
RESEND_AUDIENCE_ID
only needed when Newsletter subscription management is required
📤 Core Send Function Explained
sendEmail Function Signature
NextDevKit provides a unified sendEmail
function that supports both template emails and custom HTML emails:
export async function sendEmail<T extends TemplateKey>(
params: {
to: string;
locale?: Locale;
} & (
| {
templateKey: T; // Use template
context: {...}; // Template parameters
}
| {
subject: string; // Custom email
html?: string; // HTML content
text?: string; // Plain text content
}
),
): Promise<boolean> // Returns send success status
Two Sending Methods
Method 1: Using Templates (Recommended)
import { sendEmail } from "@/mail";
// Send welcome email
const success = await sendEmail({
to: "user@example.com",
templateKey: "welcome",
context: {
name: "John Doe",
},
locale: "en", // Optional, supports multi-language
});
Method 2: Custom HTML
import { sendEmail } from "@/mail";
const success = await sendEmail({
to: "user@example.com",
subject: "Order Confirmation",
html: `<h1>Your order is confirmed</h1><p>Order #12345</p>`,
text: "Your order is confirmed, Order #12345", // Fallback plain text
});
Sending Process Explanation
NextDevKit's email sending process:
- Parameter Validation: Check email format and required parameters
- Template Rendering: If using templates, render React components to HTML
- Internationalization: Load translation files for the corresponding language
- Provider Sending: Send through Resend or other configured service providers
- Error Handling: Return sending status, log errors
📧 Newsletter Subscription Management
Core Newsletter Actions
NextDevKit provides three core Newsletter management Server Actions:
import { subscribeToNewsletter, unsubscribeFromNewsletter, isSubscribedToNewsletter } from "@/mail/actions";
// 1. Subscribe to Newsletter
await subscribeToNewsletter({ email: "user@example.com" });
// 2. Unsubscribe
await unsubscribeFromNewsletter({ email: "user@example.com" });
// 3. Check subscription status
const isSubscribed = await isSubscribedToNewsletter({ email: "user@example.com" });
Newsletter How It Works
- Resend Audience Integration: Use
RESEND_AUDIENCE_ID
to manage subscription lists - Auto Deduplication: Duplicate subscriptions will update existing contact status
- Unsubscribe Management: Support one-click unsubscribe and re-subscription
- Status Tracking: Can query subscription status for any email
Key Configuration:
- Ensure an Audience is created in Resend
- Set
RESEND_AUDIENCE_ID
environment variable - Newsletter template automatically sends confirmation email after successful subscription
🎨 Creating Custom Templates
If existing templates don't meet your needs, you can create custom email templates.
Custom Template Basic Structure
import EmailLayout from "@/mail/components/layout";
import EmailButton from "@/mail/components/email-button";
import type { i18nEmailProps } from "@/mail/types";
import { Text, Heading } from "@react-email/components";
import { createTranslator } from "use-intl/core";
export function MyCustomEmail({
userName,
actionUrl,
locale,
messages,
}: {
userName: string;
actionUrl: string;
} & i18nEmailProps) {
const t = createTranslator({
locale,
messages,
});
return (
<EmailLayout>
<Heading className="text-2xl font-bold">
Custom Email Title
</Heading>
<Text>
Hello {userName}, this is a custom email.
</Text>
<EmailButton href={actionUrl}>
Take Action
</EmailButton>
</EmailLayout>
);
}
Register and Use Template
Step 1: Register in src/mail/templates/index.ts
import { MyCustomEmail } from "./my-custom-email";
export const mailTemplates = {
// ... other templates
myCustomEmail: MyCustomEmail, // Add new template
} as const;
Step 2: Use new template
const success = await sendEmail({
to: "user@example.com",
templateKey: "myCustomEmail",
context: {
userName: "John Doe",
actionUrl: "https://example.com/action",
},
locale: "en",
});
Template Design Points
Follow Tailwind Design System:
- Use
EmailLayout
,EmailButton
and more custom email components for consistency - Utilize Tailwind class names:
mb-6
,font-semibold
,text-muted-foreground
- Support internationalization: support multiple languages
🔌 Custom Email Provider
Besides Resend, you can integrate other email service providers.
Provider Interface Design
export interface EmailProvider {
send(params: {
to: string;
subject: string;
html: string;
text?: string;
}): Promise<boolean>;
subscribe?(email: string): Promise<void>;
unsubscribe?(email: string): Promise<void>;
isSubscribed?(email: string): Promise<boolean>;
}
Create Custom Provider
import sgMail from "@sendgrid/mail";
import type { EmailProvider } from "@/mail/types";
export class SendGridProvider implements EmailProvider {
constructor() {
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
}
async send(params: {
to: string;
subject: string;
html: string;
text?: string;
}): Promise<boolean> {
try {
await sgMail.send({
to: params.to,
from: process.env.SENDGRID_FROM_EMAIL!,
subject: params.subject,
html: params.html,
text: params.text,
});
return true;
} catch (error) {
console.error("SendGrid error:", error);
return false;
}
}
// Optional: implement Newsletter functionality
async subscribe(email: string): Promise<void> {
// Call SendGrid Contacts API
}
}
Register New Provider
import { ResendProvider } from "./resend";
import { SendGridProvider } from "./sendgrid";
export function getMailProvider() {
const provider = process.env.MAIL_PROVIDER || "resend";
switch (provider) {
case "sendgrid":
return new SendGridProvider();
case "resend":
default:
return new ResendProvider();
}
}
Key Advantages:
- Flexible Switching: Control email service provider through environment variables
- Unified Interface: All providers implement the same interface
- Progressive Migration: Can seamlessly switch email service providers
🌍 Multi-language Email
NextDevKit has built-in i18n support, configure translations in messages/en.json
:
{
"mail": {
"welcome": {
"greeting": "Hello {name}!",
"intro": "Welcome to our community."
}
}
}
Send emails in specified language:
await sendEmail({
to: "user@example.com",
templateKey: "welcome",
context: { name: "John Doe" },
locale: "en" // Specify language
});
📚 Summary
Now you've mastered NextDevKit's modern email system:
🎯 Core Achievements
Tailwind + React Email: Break free from traditional inline styles, use modern class names to develop email templates
Three Email Design Patterns:
- Operational Email: Simple and direct, single action button
- Transactional Email: Multi-step structure, rich information display
- Marketing Email: Visually prominent, branded design
System Extension Capabilities:
- Custom template creation and registration
- Custom Provider integration (SendGrid, Postmark, etc.)
- Multi-language support and Newsletter management
Reference Resources: