LogoNEXTDEVKIT Docs

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

Comparison: Traditional vs React Email
// ❌ 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 vs Tailwind
// ❌ 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.

Tailwind + React Email Practice
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

index.ts
actions.ts
types.ts
layout.tsx
email-button.tsx
newsletter-signup.tsx
forgot-password.tsx
email-verification.tsx
contact-form.tsx
index.ts
resend.ts
index.ts

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

.env.local
# 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

src/config/index.ts
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:

sendEmail function type
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)

Send email using template
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

Send custom email
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:

  1. Parameter Validation: Check email format and required parameters
  2. Template Rendering: If using templates, render React components to HTML
  3. Internationalization: Load translation files for the corresponding language
  4. Provider Sending: Send through Resend or other configured service providers
  5. Error Handling: Return sending status, log errors

📧 Newsletter Subscription Management

Core Newsletter Actions

NextDevKit provides three core Newsletter management Server Actions:

Newsletter subscription management
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

  1. Resend Audience Integration: Use RESEND_AUDIENCE_ID to manage subscription lists
  2. Auto Deduplication: Duplicate subscriptions will update existing contact status
  3. Unsubscribe Management: Support one-click unsubscribe and re-subscription
  4. 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

src/mail/templates/my-custom-email.tsx
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

Register new template
import { MyCustomEmail } from "./my-custom-email";

export const mailTemplates = {
  // ... other templates
  myCustomEmail: MyCustomEmail, // Add new template
} as const;

Step 2: Use new template

Send custom email
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

src/mail/types.ts
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

src/mail/providers/sendgrid.ts
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

src/mail/providers/index.ts
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:

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: