· resend / react-email / email

Cách gửi transactional email với Resend và React Email

Hướng dẫn từng bước: cài Resend và React Email, viết template có type, gửi từ Next.js route, và preview trên local. Bao gồm thay đổi import trong React Email 6.

Bởi

1.404 từ · 8 phút đọc

Nếu ứng dụng TypeScript của bạn cần gửi dưới 10.000 email mỗi tháng, hãy dùng Resend kết hợp React Email. Bạn sẽ có một TypeScript SDK hiện đại, React components làm email template, free tier rộng rãi, và không cần phải vật lộn với bất kỳ ngôn ngữ XML template nào. Chỉ hai package, một lệnh npm install, là bạn đã có thể bắt đầu gửi email.

Bài viết này dành cho ai

Các developer TypeScript hoặc Next.js cần gửi transactional email (email chào mừng, đặt lại mật khẩu, hóa đơn) và muốn viết template bằng React thay vì HTML thuần hoặc drag-and-drop builder. Nếu bạn cần gửi hơn 10k email mỗi tháng, hãy xem Postmark hoặc Mailgun — pricing của họ phù hợp hơn cho lượng lớn.

Môi trường thử nghiệm

resend v6.12.4, react-email v6.5.0 (React Email 6.0 ra mắt ngày 16 tháng 4 năm 2026), Next.js 15 App Router, Node.js 22. Mọi đoạn code trong bài đều chạy đúng với các version này.

1. Tạo tài khoản Resend và API key

Đăng ký tại resend.com. Free tier cho phép 3.000 email mỗi tháng100 email mỗi ngày — giới hạn theo ngày mới là ràng buộc thực tế, không phải giới hạn theo tháng.

Sau khi vào dashboard:

  1. Vào API KeysCreate API Key.
  2. Đặt tên (ví dụ my-app-dev), chọn quyền Sending access.
  3. Copy key ngay — bạn chỉ thấy nó một lần duy nhất.

Thêm vào .env.local:

RESEND_API_KEY=re_...

Lưu ý: Resend chỉ hiển thị key ngay lúc tạo. Nếu bạn chuyển trang trước khi copy, hãy thu hồi key đó và tạo key mới.

2. Thêm và xác minh domain

Địa chỉ gửi [email protected] dùng được khi test ban đầu, nhưng mọi email gửi đến địa chỉ không thuộc tài khoản Resend của bạn sẽ bị từ chối khi đưa lên production. Bạn cần một custom domain đã được xác minh.

Trong dashboard, vào DomainsAdd Domain và nhập domain (ví dụ yourdomain.com). Resend sẽ cung cấp ba DNS record để thêm:

LoạiMục đích
TXTSPF — xác nhận Resend được phép gửi thay bạn
TXT (DKIM)DKIM — chữ ký mã hóa để tăng khả năng vào inbox
MXCần cho theo dõi bounce; không bắt buộc nhưng nên có

Copy giá trị từng record trực tiếp từ dashboard — các giá trị này riêng theo tài khoản và khác nhau mỗi domain. Sau khi thêm đủ ba record, nhấn Verify. DNS propagation có thể mất đến 72 tiếng, nhưng thường xong trong vòng 15 phút.

Lưu ý: gửi từ [email protected] trên production sẽ âm thầm thất bại với người nhận không thuộc tài khoản bạn. Dashboard vẫn báo delivered, nhưng email thực ra không đến nơi. Hãy thêm domain trước khi go live.

3. Cài đặt package

npm install resend react-email

Chỉ vậy thôi. Đừng cài @react-email/components — package này đã bị deprecated khi React Email 6.0 ra mắt. Toàn bộ component import giờ đến từ react-email trực tiếp. Mọi tutorial đăng trước tháng 4 năm 2026 mà hướng dẫn cài @react-email/components đều không còn đúng.

Lưu ý: nếu cài cả @react-email/components lẫn react-email, bạn sẽ có hai version xung đột của cùng một component và lỗi TypeScript rất khó debug. Gỡ nó đi.

4. Viết React Email template

Tạo emails/WelcomeEmail.tsx:

import {
  Html,
  Head,
  Body,
  Container,
  Heading,
  Text,
  Button,
} from 'react-email'

interface WelcomeEmailProps {
  username: string
  loginUrl: string
}

export function WelcomeEmail({ username, loginUrl }: WelcomeEmailProps) {
  return (
    <Html>
      <Head />
      <Body style={{ fontFamily: 'sans-serif', background: '#f4f4f5' }}>
        <Container
          style={{
            maxWidth: '560px',
            margin: '40px auto',
            background: '#fff',
            borderRadius: '8px',
            padding: '32px',
          }}
        >
          <Heading style={{ fontSize: '24px', marginBottom: '16px' }}>
            Welcome, {username}
          </Heading>
          <Text style={{ color: '#374151', lineHeight: '1.6' }}>
            Your account is ready. Click below to sign in.
          </Text>
          <Button
            href={loginUrl}
            style={{
              background: '#000',
              color: '#fff',
              padding: '12px 24px',
              borderRadius: '6px',
              textDecoration: 'none',
              display: 'inline-block',
              marginTop: '16px',
            }}
          >
            Sign in
          </Button>
        </Container>
      </Body>
    </Html>
  )
}

Bảy import (Html, Head, Body, Container, Heading, Text, Button) đều đến từ react-email — một package, một nguồn import. React Email biên dịch component này thành HTML dạng table, render đúng trên Gmail, Outlook và Apple Mail.

Lưu ý: import từ @react-email/components thay vì react-email sẽ gây lỗi module-not-found khi runtime sau khi bạn gỡ package cũ, hoặc âm thầm load code cũ nếu để cả hai cùng tồn tại.

5. Gửi email từ Next.js API route

Tạo app/api/send/route.ts:

import { Resend } from 'resend'
import { WelcomeEmail } from '@/emails/WelcomeEmail'
import { NextResponse } from 'next/server'

const resend = new Resend(process.env.RESEND_API_KEY)

export async function POST(request: Request) {
  const { username, email, loginUrl } = await request.json()

  const { data, error } = await resend.emails.send({
    from: 'Welcome <[email protected]>',  // must match verified domain
    to: [email],
    subject: `Welcome, ${username}`,
    react: <WelcomeEmail username={username} loginUrl={loginUrl} />,
  })

  if (error) {
    return NextResponse.json({ error }, { status: 400 })
  }

  return NextResponse.json({ id: data?.id })
}

Trường react nhận component trực tiếp — không cần renderAsync hay serialize thành string. Resend render phía server. Pattern { data, error } tương tự fetch — luôn kiểm tra error trước khi dùng data.

Địa chỉ from phải dùng domain đã xác minh (yourdomain.com trong ví dụ). Display name (Welcome) tùy chọn nhưng nên có để người nhận nhận ra trong inbox.

Lưu ý: nếu from dùng domain chưa xác minh, Resend trả về lỗi 422 với thông báo The gmail.com domain is not verified. Please, add and verify your domain on https://resend.com/domains. Cách sửa lúc nào cũng vậy: xác minh domain trước.

6. Preview template trên local

Thêm script vào package.json:

{
  "scripts": {
    "email:dev": "email dev --dir ./emails --port 3001"
  }
}

Chạy:

npm run email:dev

Lệnh này mở browser tại http://localhost:3001 với live-reload preview của mọi template trong ./emails/. Bạn có thể chuyển giữa desktop và mobile view, và xem cả rendered HTML lẫn source gốc. Không cần tài khoản Resend hay API key cho bước preview này.

Lưu ý: nếu port xung đột với Next.js dev server (cả hai mặc định là 3000), truyền --port 3001 như ví dụ trên.

Những điểm cần chú ý

100 email/ngày ở free tier. Giới hạn tháng là 3.000 nghe có vẻ ổn, nhưng 100/ngày mới là trần cứng. Nếu chuỗi welcome email hoặc một đợt gửi hàng loạt chạm ngưỡng 100 trước 0h UTC, các lần gửi còn lại sẽ thất bại với lỗi 429 cho đến ngày hôm sau. Hãy tính đến điều này khi staging trước khi go live.

[email protected] chỉ dùng để test. Địa chỉ này là shared address của Resend. Dùng được khi gửi đến email Resend đã xác minh của bạn trong quá trình phát triển, nhưng bị rate-limit và bị chặn khi production. Đừng viết code gửi email dùng địa chỉ này nếu bạn định ship.

Thay đổi import ở React Email 6. Mọi tutorial cũ và câu trả lời trên Stack Overflow có import { Button } from '@react-email/components' đều sai từ tháng 4 năm 2026. Import đúng là from 'react-email'. Nếu bạn đang adapt template cũ, hãy find-replace toàn bộ from '@react-email/components'from 'react-email'.

Biến môi trường trên Cloudflare Workers. Nếu bạn deploy route gửi email lên Cloudflare Worker, process.env.RESEND_API_KEY sẽ là undefined. Dùng env.RESEND_API_KEY thay thế, trong đó env là tham số thứ hai của hàm fetch trong Worker. Cập nhật cách khởi tạo Resend client theo:

// Cloudflare Workers
export default {
  async fetch(request: Request, env: Env) {
    const resend = new Resend(env.RESEND_API_KEY)
    // ...
  }
}

Kết luận

Dùng Resend + React Email cho mọi ứng dụng TypeScript gửi dưới 10k email mỗi tháng. SDK đi kèm TypeScript type đầy đủ với return shape { data, error } gọn gàng, React template thực sự tốt hơn HTML thuần hoặc MJML, và free tier đủ dùng cho hầu hết indie app mà không cần thẻ tín dụng. Với lượng lớn hơn, Postmark và Mailgun được xây dựng cho volume và tính giá theo nghìn email — nhưng với những project gửi vài trăm email mỗi ngày, Resend là lựa chọn hợp lý nhất. Xem đánh giá đầy đủ về Resend để biết thêm về pricing và deliverability dài hạn.