はじめに
この記事では、Resendというメール送信APIサービスを利用してNext.jsアプリケーションにメール送信機能を実装する方法を解説します。
このサービスを選択した背景としては、Cloudflare Workersで使っていたMailChannelsが無料で使えなくなったため、代替サービスを探していたということがあります。Resendは小規模開発で十分な無料枠があるため、これに移行することにしました。
Resendとは
Resendは開発者体験に重きが置かれたメールを送信APIサービスです。SendGridやAmazon SESなどの既存サービスと比べると後発であるため色々と使いやすくなっています。
- 色々な言語のSDKが用意されていてドキュメントが充実
- 様々な言語とフレームワークに対応QuickStartガイド
- 無料プランで3,000通/月・100通/日まで送信可能(2024年12月現在)
アーキテクチャ概要
今回は、ResendとNext.jsのServer Actionsを組み合わせて、フォームに入力された内容をメールで送信するサイトを構築します。
今回構築するサイトの構成は以下のとおりです。メールを送信するためのサーバーサイドもNext.js のServer Actionsを使って実現します。
@react-email
)と Next.js 15+React 19の組み合わせにおいて、Edgeランタイム(Server Actionsで利用)上で正常に動かない問題があるため、Next.js 14を使用します。(関連Issue)- フロントエンド:Next.js 14 (App Router) + React 18
- バックエンド:Next.js 14 (Server Actions)
環境構築
Resendのアカウント作成とAPIキーの取得
- Resendのサインアップページでアカウントを作成します。メールアドレス+パスワードの他に、GitHubやGoogleアカウントでのサインアップが可能です。
- 管理画面のDomainsで、送信元アドレスに使うドメインを登録します。登録後、表示されたDNSレコードをドメインのDNSに追加します。
- 管理画面のAPI KeysでAPIキーを生成します。PermissionはSending Accessを選択します。後で環境変数に入れて使うので、表示されたAPIキーをメモしておきます。
Next.jsプロジェクトのセットアップ
create-next-app
コマンドでNext.js 14のプロジェクトを新規作成します。npx create-next-app@14 resend-nextjs ✔ Would you like to use TypeScript? … No / Yes ✔ Would you like to use ESLint? … No / Yes ✔ Would you like to use Tailwind CSS? … No / Yes ✔ Would you like to use `src/` directory? … No / Yes ✔ Would you like to use App Router? (recommended) … No / Yes ✔ Would you like to customize the default import alias (@/*)? … No / Yes
- 初期テンプレートのグローバルCSSスタイルを削除します。
@tailwind base; @tailwind components; @tailwind utilities; /* これより下を全部削除 */
- 公式SDK(
resend
)をインストールします。cd resend-nextjs pnpm add resend
- .env.development.localを作成し、先ほどメモしたResendのAPIキーを環境変数に設定します。
RESEND_API_KEY=re_123456789
Server Actionsの実装(バックエンド)
メールテンプレートの作成
メール本文のテンプレートを作成します。このように、Resend APIではHTMLメールの本文をJSX(tsx)構文でReactコンポーネントとして作成でき、styleを使った装飾なども可能です。
import { Fragment } from 'react';
interface EmailTemplateProps {
name: string;
message: string;
}
export const EmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({
name,
message,
}) => (
<div style={{ color: '#333' }}>
<p>以下の内容でお問い合わせを受信しました。</p>
<p>
<strong>お名前:</strong>
</p>
<p
style={{
margin: '1em 0',
padding: '0.5em',
border: '1px solid #ccc',
backgroundColor: '#f9f9f9',
}}
>
{name}
</p>
<p>
<strong>お問い合わせ内容:</strong>
</p>
<p
style={{
margin: '1em 0',
padding: '0.5em',
border: '1px solid #ccc',
backgroundColor: '#f9f9f9',
}}
>
{/* 改行を<br />に変換 */}
{message.split(/\r?\n/g).map((line, index) => (
<Fragment key={index}>
{line}
<br />
</Fragment>
))}
</p>
</div>
);
Resend APIを使ったメール送信関数の作成
Resend APIを使ってメールを送信する関数を作成します。APIキーは設定した環境変数から取得します。
宛先にResendのテスト用アドレスを指定しているため、送ったメールは管理画面でのみ確認ができます。必要に応じて自分のメールアドレスなどに変更してください。
import 'server-only'; // サーバーサイドのみで実行することを明示(任意)
import { Resend } from 'resend';
import { EmailTemplate } from '@/components/EmailTemplate';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendEmail({
name,
message,
}: {
readonly name: string;
readonly message: string;
}) {
try {
const { error } = await resend.emails.send({
from: 'Acme <[email protected]>',
to: ['[email protected]'],
subject: 'お問い合わせ',
react: EmailTemplate({ name, message }),
});
if (error) {
console.error('Failed to send email', error);
throw new Error('Failed to send email');
}
} catch (error) {
console.error('Failed to send email', error);
throw new Error('Failed to send email');
}
}
Server Action関数の作成
Server Actions関数を実装します。Server Actionsはサーバーサイドで実行されるので、Resend APIを使ったメール送信が可能です。実運用ではformDataに対してバリデーションやBot検証を行う必要があります。
'use server';
import { sendEmail } from '@/lib/send-mail';
export async function submit(formData: FormData) {
const name = formData.get('name')!.toString();
const message = formData.get('message')!.toString();
await sendEmail({ name, message });
}
フォームコンポーネントの作成
入力フォームの作成
入力フォームを作成します。ステート(useFormStatus)を使うのでフォームはClient Componentで作成します。スタイルはTailwindCSSで作成しています。formタグのactions内で作成したServer Actions関数(submit)を呼ぶことで、サーバーサイドでメール送信処理が走ります。
import ContactForm from './ContactForm';
export default function Home() {
return (
<main className="flex flex-row justify-center p-8">
<ContactForm />
</main>
);
}
'use client';
import { useFormStatus } from 'react-dom';
import { submit } from './actions';
export default function ContactForm() {
return (
<form
className="w-full max-w-md"
action={async (formData) => {
await submit(formData);
alert('お問い合わせを受け付けました。');
}}
>
<div className="mb-4">
<label htmlFor="name" className="text-sm font-medium">
お名前
</label>
<input
type="text"
id="name"
name="name"
className="mt-1 w-full rounded-md border border-gray-300 p-2"
required
/>
</div>
<div className="mb-4">
<label htmlFor="message" className="text-sm font-medium">
お問い合わせ内容
</label>
<textarea
id="message"
name="message"
className="mt-1 w-full rounded-md border border-gray-300 p-2"
rows={10}
required
/>
</div>
<SubmitButton />
</form>
);
}
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button
type="submit"
className="rounded-md bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 disabled:bg-gray-400"
disabled={pending}
>
{pending ? '送信中…' : '送信'}
</button>
);
}
動作確認
npm run dev
で開発サーバを立ち上げて、フォームにテストデータを入力して送信してみます。
フォームから正常に送信できたら、Resendの管理画面で送信したメールを確認できるはずです。もし宛先をテスト用アドレスから変更していれば、そのアドレスにメールが送られてきているはずです。
まとめ
Resendを利用したNext.jsでのメール送信機能の実装について解説しました。
個人的に思ったResendの利点を:
- ドキュメントが豊富で、SDKが使いやすい
- React (JSX構文) でHTMLメールが簡単に作成できる
- 小規模であれば十分使える無料枠
もう少し複雑なメール本文を作成したければ、同じくResend社が開発しているreact-emailライブラリを使うことでTailwindCSSや既成のコンポーネントを使った高度なメールが容易に作成できます。