ANTIGRAVITY LABEN
記事一覧/連携・プラグイン
連携・プラグイン/2026-03-26中級

Antigravity × Resend + React Email でトランザクションメール自動化 — 美しいメールテンプレートをAIで構築・配信する

Antigravity IDE と Resend + React Email を使い、型安全で美しいトランザクションメールをAIアシストで構築・配信する方法を解説。ウェルカムメール・決済確認・パスワードリセットの実装例付き。

antigravity435resendreact-emailemail-automationtypescript26next.js4integrations20

メール実装が地味に消耗する理由

決済確認やパスワードリセットのメールが、送信先のクライアントによって崩れて表示される。テーブルレイアウトを手作業で組み直しては、また別のクライアントで崩れる。トランザクションメールの実装は、地味でありながら消耗しやすい作業です。

Antigravity のエージェント機能に Resend と React Email を組み合わせると、この消耗を大きく減らせます。型安全で保守しやすいメール配信の仕組みを、テンプレート生成からAPI実装、テスト送信まで一つの流れで構築していきます。

Resend と React Email とは

Resend

Resend は、開発者ファーストなメール配信APIサービスです。従来のメール配信サービス(SendGrid, Mailgun など)と比較して、以下の特徴があります。

  • シンプルなAPI: REST API と公式SDK(Node.js / Python / Go / Ruby)で即座に送信可能
  • React Email との統合: JSXでメールテンプレートを書き、そのまま送信できる
  • 高い配信到達率: SPF / DKIM / DMARC を自動設定
  • リアルタイム分析: 開封率・クリック率・バウンス率をダッシュボードで確認

React Email

React Email は、Reactコンポーネントでメールテンプレートを構築するライブラリです。HTMLメール特有の <table> レイアウトや各メールクライアントの互換性を、コンポーネントが内部で吸収してくれます。

// React Email の基本コンポーネント例
import { Html, Head, Body, Container, Text } from "@react-email/components";
 
// 各コンポーネントがメールクライアント互換のHTMLを自動生成
// <Container> → 中央揃えの <table> に変換
// <Text> → Outlook対応の <p> タグに変換

環境構築

前提条件

  • Node.js 20 以上
  • Antigravity IDE 最新版
  • Resend アカウント(無料枠: 月3,000通)
  • Next.js プロジェクト(App Router 推奨)

パッケージのインストール

Antigravity のターミナルを開き、以下を実行します。

# Resend SDK と React Email コンポーネントをインストール
npm install resend @react-email/components
 
# 開発用プレビューサーバー
npm install -D react-email
 
# 期待する出力:
# added 42 packages in 8s

環境変数の設定

プロジェクトルートの .env.local に Resend API キーを追加します。

# .env.local
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Tip: Antigravity のインラインチャット(Cmd+I)で「.env.local に Resend の API キー用エントリを追加して」と指示すれば、適切な形式で自動追加してくれます。

メールテンプレートの構築

ディレクトリ構成

src/
├── emails/                  # メールテンプレート
│   ├── welcome.tsx          # ウェルカムメール
│   ├── payment-confirm.tsx  # 決済確認メール
│   └── password-reset.tsx   # パスワードリセット
├── lib/
│   └── email.ts             # Resend クライアント設定
└── app/
    └── api/
        └── send-email/
            └── route.ts     # メール送信 API

ウェルカムメールテンプレート

Antigravity のエージェントに「React Email でウェルカムメールテンプレートを作成して」と指示すれば、以下のようなコンポーネントが生成されます。

// src/emails/welcome.tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Text,
  Button,
  Img,
  Hr,
} from "@react-email/components";
 
interface WelcomeEmailProps {
  userName: string;
  loginUrl: string;
}
 
export default function WelcomeEmail({
  userName = "ユーザー",
  loginUrl = "https://example.com/login",
}: WelcomeEmailProps) {
  return (
    <Html lang="ja">
      <Head />
      {/* プレビューテキスト: 受信箱で件名の横に表示される */}
      <Preview>{userName}さん、ご登録ありがとうございます!</Preview>
      <Body style={main}>
        <Container style={container}>
          <Img
            src="https://example.com/logo.png"
            width={48}
            height={48}
            alt="Logo"
          />
          <Text style={heading}>ようこそ、{userName}さん!</Text>
          <Text style={paragraph}>
            アカウントの登録が完了しました。さっそくログインして、
            すべての機能をお試しください。
          </Text>
          <Section style={buttonContainer}>
            <Button style={button} href={loginUrl}>
              ログインする
            </Button>
          </Section>
          <Hr style={hr} />
          <Text style={footer}>
            このメールに心当たりがない場合は、このまま無視してください。
          </Text>
        </Container>
      </Body>
    </Html>
  );
}
 
// スタイル定義(インラインスタイルが必須)
const main = {
  backgroundColor: "#f6f9fc",
  fontFamily: '-apple-system, "Segoe UI", Roboto, sans-serif',
};
const container = {
  margin: "0 auto",
  padding: "40px 20px",
  maxWidth: "560px",
};
const heading = {
  fontSize: "24px",
  fontWeight: "bold" as const,
  color: "#1a1a1a",
  marginBottom: "16px",
};
const paragraph = {
  fontSize: "16px",
  lineHeight: "1.6",
  color: "#4a4a4a",
};
const buttonContainer = { textAlign: "center" as const, margin: "32px 0" };
const button = {
  backgroundColor: "#4f46e5",
  color: "#ffffff",
  padding: "12px 32px",
  borderRadius: "8px",
  fontSize: "16px",
  textDecoration: "none",
};
const hr = { borderColor: "#e5e7eb", margin: "24px 0" };
const footer = { fontSize: "12px", color: "#9ca3af" };

決済確認メールテンプレート

// src/emails/payment-confirm.tsx
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Section,
  Row,
  Column,
  Text,
  Hr,
} from "@react-email/components";
 
interface PaymentConfirmProps {
  userName: string;
  planName: string;
  amount: string;
  currency: string;
  receiptUrl: string;
}
 
export default function PaymentConfirmEmail({
  userName = "ユーザー",
  planName = "Pro プラン",
  amount = "¥380",
  currency = "JPY",
  receiptUrl = "#",
}: PaymentConfirmProps) {
  return (
    <Html lang="ja">
      <Head />
      <Preview>お支払い確認: {planName}{amount}</Preview>
      <Body style={main}>
        <Container style={container}>
          <Text style={heading}>お支払い完了</Text>
          <Text style={paragraph}>
            {userName}さん、{planName}のお支払いが正常に処理されました。
          </Text>
          <Section style={receiptBox}>
            <Row>
              <Column><Text style={label}>プラン</Text></Column>
              <Column><Text style={value}>{planName}</Text></Column>
            </Row>
            <Hr style={hr} />
            <Row>
              <Column><Text style={label}>金額</Text></Column>
              <Column><Text style={value}>{amount}{currency})</Text></Column>
            </Row>
          </Section>
          <Text style={footerText}>
            領収書は<a href={receiptUrl} style={link}>こちら</a>から
            ダウンロードできます。
          </Text>
        </Container>
      </Body>
    </Html>
  );
}
 
const main = {
  backgroundColor: "#f6f9fc",
  fontFamily: '-apple-system, "Segoe UI", Roboto, sans-serif',
};
const container = { margin: "0 auto", padding: "40px 20px", maxWidth: "560px" };
const heading = { fontSize: "24px", fontWeight: "bold" as const, color: "#1a1a1a" };
const paragraph = { fontSize: "16px", lineHeight: "1.6", color: "#4a4a4a" };
const receiptBox = {
  backgroundColor: "#ffffff",
  border: "1px solid #e5e7eb",
  borderRadius: "8px",
  padding: "24px",
  margin: "24px 0",
};
const label = { fontSize: "14px", color: "#6b7280" };
const value = { fontSize: "14px", fontWeight: "bold" as const, textAlign: "right" as const };
const hr = { borderColor: "#e5e7eb" };
const footerText = { fontSize: "14px", color: "#6b7280" };
const link = { color: "#4f46e5" };

Resend クライアントとAPI実装

Resend クライアント設定

// src/lib/email.ts
import { Resend } from "resend";
 
// シングルトンパターンで Resend クライアントを初期化
// 環境変数が未設定の場合は早期エラーで検知
if (!process.env.RESEND_API_KEY) {
  throw new Error("RESEND_API_KEY is not set");
}
 
export const resend = new Resend(process.env.RESEND_API_KEY);

メール送信APIエンドポイント

// src/app/api/send-email/route.ts
import { NextRequest, NextResponse } from "next/server";
import { resend } from "@/lib/email";
import WelcomeEmail from "@/emails/welcome";
import PaymentConfirmEmail from "@/emails/payment-confirm";
 
// メールタイプに応じたテンプレートのマッピング
const EMAIL_TEMPLATES = {
  welcome: {
    subject: "ご登録ありがとうございます!",
    component: WelcomeEmail,
  },
  "payment-confirm": {
    subject: "お支払い確認",
    component: PaymentConfirmEmail,
  },
} as const;
 
type EmailType = keyof typeof EMAIL_TEMPLATES;
 
export async function POST(req: NextRequest) {
  try {
    const { type, to, props } = await req.json();
 
    // バリデーション
    if (!type || !to || !EMAIL_TEMPLATES[type as EmailType]) {
      return NextResponse.json(
        { error: "Invalid request: type and to are required" },
        { status: 400 }
      );
    }
 
    const template = EMAIL_TEMPLATES[type as EmailType];
    const Component = template.component;
 
    // Resend API でメール送信
    const { data, error } = await resend.emails.send({
      from: "MyApp <noreply@yourdomain.com>",
      to: [to],
      subject: template.subject,
      react: Component(props || {}),
    });
 
    if (error) {
      console.error("Email send error:", error);
      return NextResponse.json({ error: error.message }, { status: 500 });
    }
 
    // 期待する出力: { id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }
    return NextResponse.json({ success: true, id: data?.id });
  } catch (err) {
    console.error("Unexpected error:", err);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 }
    );
  }
}

Antigravity エージェントによる開発の効率化

Antigravity IDE の最大の強みは、エージェント機能を使ったAIアシスト開発です。メールテンプレートの構築では特に威力を発揮します。

テンプレートの自動生成

Antigravity のチャットパネルで以下のように指示するだけで、React Email のテンプレートが生成されます。

パスワードリセット用のメールテンプレートを React Email で作成してください。
- リセットリンクのボタンを中央に配置
- リンクの有効期限(1時間)を明記
- ブランドカラーは #4f46e5
- 日本語で作成

エージェントは React Email のコンポーネントAPIを理解しているため、メールクライアント互換の正しいHTMLを出力します。

プレビューとテスト

React Email には開発用のプレビューサーバーが付属しています。

# プレビューサーバーを起動
npx react-email dev --dir src/emails --port 3001
 
# 期待する出力:
# Ready on http://localhost:3001
# Found 3 email templates

ブラウザで http://localhost:3001 を開くと、テンプレート一覧が表示され、各メールのプレビューと props の動的変更がリアルタイムで確認できます。

Resend テスト送信

開発中は Resend のテストモードを使えば、実際のメールを送らずに API の動作確認ができます。

# curl でテスト送信
curl -X POST http://localhost:3000/api/send-email \
  -H "Content-Type: application/json" \
  -d '{
    "type": "welcome",
    "to": "test@example.com",
    "props": {
      "userName": "テスト太郎",
      "loginUrl": "https://example.com/login"
    }
  }'
 
# 期待する出力:
# {"success":true,"id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}

Stripe 決済後の自動メール送信

SaaSアプリで特に需要が高いのが、Stripe 決済完了後の自動確認メール送信です。Webhook と組み合わせることで完全に自動化できます。実際の Stripe 連携の詳細については「Stripe × Antigravity 決済実装ガイド」も参考にしてください。

// src/app/api/webhook/route.ts(抜粋)
import { resend } from "@/lib/email";
import PaymentConfirmEmail from "@/emails/payment-confirm";
 
// Stripe Webhook: checkout.session.completed 時にメール送信
async function handleCheckoutCompleted(session: any) {
  const email = session.customer_details?.email;
  if (!email) return;
 
  await resend.emails.send({
    from: "MyApp <noreply@yourdomain.com>",
    to: [email],
    subject: "お支払い確認 — MyApp",
    react: PaymentConfirmEmail({
      userName: session.customer_details.name || "お客様",
      planName: session.metadata?.plan_type === "pro" ? "Pro プラン" : "Premium プラン",
      amount: `¥${session.amount_total / 100}`,
      currency: session.currency?.toUpperCase() || "JPY",
      receiptUrl: session.receipt_url || "#",
    }),
  });
}

本番環境へのデプロイ

DNS 設定(独自ドメイン送信)

Resend で独自ドメインからメールを送信するには、DNS に以下のレコードを追加します。

# Resend ダッシュボード → Domains → Add Domain で表示される値を設定
TXT   _resend        re_xxxx...  # ドメイン認証
CNAME send._domainkey ...         # DKIM 署名
TXT   _dmarc         v=DMARC1; p=none; ...  # DMARC ポリシー

Cloudflare Workers での注意点

Cloudflare Workers 環境で Resend を使う場合は、fetch ベースの HTTP クライアントが使用されるため、特別な設定は不要です。ただし、環境変数は wrangler secret put RESEND_API_KEY で設定してください。

サーバーレスAPIの構築パターンについては「Antigravity × Hono + Cloudflare Workers サーバーレスAPI開発ガイド」が参考になります。

まとめ

Antigravity IDE と Resend + React Email を組み合わせることで、トランザクションメールの開発・配信を大幅に効率化できます。特に Antigravity のエージェント機能は、メールテンプレートのコーディングという「地味だが手間のかかる」作業を劇的に短縮します。

今回紹介した構成のポイントをまとめると、React Email によるコンポーネントベースのテンプレート設計、Resend API を使ったシンプルな配信ロジック、Stripe Webhook との連携による完全自動化、そして Antigravity エージェントによるテンプレート生成の加速です。

Next.js でのフルスタック開発

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

連携・プラグイン2026-06-28
無人で回したエージェントの変更を、どこから見るか — 再入場のためのダイジェスト
無人で動かしたエージェントが残した大量の変更を、朝、どこから見るか。全 diff でもチャットログでもなく、リスク階級で束ねた再入場ダイジェストを設計します。
連携・プラグイン2026-05-30
Antigravity に Crashlytics のスタックトレースを渡して直す — 4本のアプリで3週間運用した所感
Crashlytics に上がってくるクラッシュを、Antigravity にスタックトレースごと渡して原因を絞り、修正まで回す。壁紙アプリ4本で3週間続けてみて見えてきた、任せられる範囲と握っておくべき判断を実装に沿って書きました。
連携・プラグイン2026-05-19
Antigravity から wrangler tail を呼ばせて Cloudflare Workers の障害切り分けを3週間任せた所感
6サイトを Cloudflare Workers で運用していて、エラー対応が朝の時間を侵食していました。Antigravity に wrangler tail を呼ばせ、ログ判読と一次切り分けを3週間任せた記録です。任せた範囲、任せなかった範囲、想定外だった挙動を率直にまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →