1. Next.jsプロジェクトの作成
まずは、新規Next.jsプロジェクトを作成します。以下のコマンドを実行して、プロジェクトディレクトリを用意してください。
npx create-next-app contact-form-with-vercel
cd contact-form-with-vercel
2. フロントエンドフォームの作成
次に、ユーザーから名前、メールアドレス、メッセージを入力してもらうフォームを実装します。pages/contact.js
(またはTypeScriptの場合は.tsx
)に以下のコードを追加してください。フォームは送信時にPOSTリクエストをAPIルートへ送るようにしています。
import { useState } from "react";
export default function Contact() {
const [form, setForm] = useState({ name: "", email: "", message: "" });
const [status, setStatus] = useState("");
const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault();
setStatus("送信中...");
// ★★ ここでreCAPTCHAトークンを取得する処理を後ほど追加可能 ★★
try {
const response = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(form),
});
if (response.ok) {
setStatus("送信成功!");
setForm({ name: "", email: "", message: "" });
} else {
setStatus("送信失敗。もう一度お試しください。");
}
} catch (error) {
setStatus("エラーが発生しました。");
}
};
return (
<div>
<h1>お問い合わせフォーム</h1>
<form onSubmit={handleSubmit}>
<label>
お名前:
<input
type="text"
name="name"
value={form.name}
onChange={handleChange}
required
/>
</label>
<br />
<label>
メールアドレス:
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
required
/>
</label>
<br />
<label>
メッセージ:
<textarea
name="message"
value={form.message}
onChange={handleChange}
required
></textarea>
</label>
<br />
{/* ★★ Honeypotフィールド ★★ */}
<input type="text" name="honeypot" style={{ display: "none" }} />
<button type="submit">送信</button>
</form>
<p>{status}</p>
</div>
);
}
3. バックエンドAPIルートの作成
pages/api/contact.js
にAPIルートを用意し、フォームから送信されたデータを処理します。ここでは、Nodemailer を使用してメール送信を実装します。また、スパム対策として後述するreCAPTCHAやHoneypotの検証処理も組み込みます。
APIルートのコード例
import nodemailer from "nodemailer";
import fetch from "node-fetch";
export default async function handler(req, res) {
if (req.method === "POST") {
const { name, email, message, recaptchaToken, honeypot } = req.body;
// Honeypotフィールドが埋まっていたらスパムとしてリジェクト
if (honeypot) {
return res.status(400).json({ error: "スパムが検出されました。" });
}
// reCAPTCHAが導入されている場合、トークンの検証を実行
if (recaptchaToken) {
const verificationResponse = await fetch(
"https://www.google.com/recaptcha/api/siteverify",
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${recaptchaToken}`,
}
);
const verificationResult = await verificationResponse.json();
if (!verificationResult.success) {
return res.status(400).json({ error: "reCAPTCHA検証に失敗しました。" });
}
}
// Nodemailerの設定
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: process.env.SMTP_SECURE === "true",
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});
try {
await transporter.sendMail({
from: process.env.SMTP_USER,
to: process.env.CONTACT_RECEIVER,
subject: `お問い合わせ:${name}`,
text: `名前: ${name}\nメールアドレス: ${email}\nメッセージ:\n${message}`,
});
res.status(200).json({ success: true });
} catch (error) {
console.error("メール送信エラー:", error);
res.status(500).json({ error: "メール送信に失敗しました。" });
}
} else {
res.status(405).json({ error: "メソッドが許可されていません。" });
}
}
環境変数の設定
ルートディレクトリに .env.local
ファイルを作成し、以下のようにSMTPサーバー情報やreCAPTCHAのシークレットキーを設定してください。
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-smtp-user@example.com
SMTP_PASS=your-smtp-password
CONTACT_RECEIVER=receiver@example.com
RECAPTCHA_SECRET_KEY=your-recaptcha-secret-key
4. スパム対策の追加
ここでは、無料で利用できる2種類のスパム防止策を紹介します。
4.1 Google reCAPTCHA v3 の導入
Google reCAPTCHAは、スパム対策として非常に有効です。以下の手順で実装します。
-
Google reCAPTCHAに登録
Google reCAPTCHAのサイトで、サイトキーとシークレットキーを取得してください。 -
フロントエンドでのトークン取得
フォーム送信時にgrecaptcha.execute
を呼び出し、トークンを取得してAPIルートへ送信します。例えば、handleSubmit
内に以下の処理を追加します。const handleSubmit = async (e) => { e.preventDefault(); setStatus("送信中..."); // reCAPTCHAトークンの取得(事前にGoogleのスクリプトを読み込む必要あり) const token = await grecaptcha.execute("YOUR_SITE_KEY", { action: "submit" }); try { const response = await fetch("/api/contact", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...form, recaptchaToken: token, honeypot: "" }), }); if (response.ok) { setStatus("送信成功!"); setForm({ name: "", email: "", message: "" }); } else { setStatus("送信失敗。もう一度お試しください。"); } } catch (error) { setStatus("エラーが発生しました。"); } };
-
API側でのトークン検証
前述のAPIルートコード内で、recaptchaToken
の検証を実施しています。検証に成功すれば、メール送信処理へ進む仕組みです。
4.2 Honeypotフィールドの導入
Honeypotは、通常ユーザーには見えない隠しフィールドを利用してボット対策を行うシンプルな方法です。
- フロントエンドには隠しフィールドを追加(上記フォーム内に実装済み)
- APIルートでフィールドの値が空でない場合は、リクエストを却下
if (honeypot) {
return res.status(400).json({ error: "スパムが検出されました。" });
}
5. Vercelへのデプロイ
最後に、完成したプロジェクトをVercelにデプロイします。Vercel CLIやGitHub連携を利用してデプロイ可能です。CLIを用いる場合は、以下のコマンドを実行してください。
vercel deploy
フォーム送信フローの図解
以下のシンプルなフローチャートは、フォーム送信からメール送信までの流れを示しています。
結論
この記事では、Next.jsとVercelを利用して基本的なお問い合わせフォームを構築し、ノード環境でのメール送信(Nodemailer)とともに、スパム対策としてGoogle reCAPTCHA v3およびHoneypotを組み込む方法を解説しました。
- Next.jsのページとAPIルートを組み合わせることで、シンプルながら柔軟性の高いフォーム実装が可能です。
- スパム対策は、ユーザー体験を損なうことなく、確実にボットからの不正送信を防止します。
この手順を参考に、実際のプロジェクトでお問い合わせフォームを実装してみてください。エンジニア初心者でも取り組みやすい内容になっていますので、ぜひ挑戦してみましょう!
💖 ご支援いただけませんか?
このブログでは、高品質な情報提供と学習活動を通じて、読者の皆さまのお役に立つことを目指しています。もしこの記事が役立ったと感じていただけましたら、ご支援いただけると幸いです!
暗号資産による寄付
以下のウォレットアドレスをご利用ください。重要:Ethereum (ETH)、BNB Chain (BNB)、Polygon (MATIC)、Avalanche (AVAX) は、全て以下の同一アドレスを使用しますが、送金ネットワークの選択を間違えると資金が失われます! 送金時には、絶対に使用するネットワーク(例: ERC-20、BEP-20、Polygon、Avalanche C-Chain)を必ず正しく選択してください。

Ethereum (ETH) (ネットワーク: ERC-20)
0x5CDA2F68f59F641B00aD172475c3d5fC10321174

BNB Chain (BNB) (ネットワーク: BEP-20)
0x5CDA2F68f59F641B00aD172475c3d5fC10321174

Polygon (MATIC) (ネットワーク: Polygon)
0x5CDA2F68f59F641B00aD172475c3d5fC10321174

Avalanche (AVAX) (ネットワーク: Avalanche C-Chain)
0x5CDA2F68f59F641B00aD172475c3d5fC10321174

Solana (SOL)
EnPFbqDbF67rU9mAPvfgh4YYtncJNbFQ9NLQ5R6z5S2f

Stellar (XLM) メモ: 必要に応じて入力してください。
GCSMWCACKVEZ737GZAV4AJRFL52ZZKVQ7M3B3KYY64JJGOAO2GDYKABO

Ripple (XRP) タグ: 必要に応じて入力してください。
r1s4EASr3zQRrfpDA3ptTahezBhGo2hhN

Cardano (ADA)
addr1q8heq6ddw8rwlqa5hqlucnfk36arah9tzc8ajxvu83870h7lrre25wzq9yemex857we56cm0xu8tmxqvm8nykmtgsjdqavdpv7

Dogecoin (DOGE)
DRFZ9JhAk3DTtu1tV85cawekWNrm1vKm3H
資金用途
寄付金は以下の目的で活用させていただきます:
- サーバー維持費やデザインツール購入
- 学習活動(オンラインコース受講・書籍購入)
- 読者向け無料コンテンツ制作
ご協力いただいた皆さまには心より感謝申し上げます! 🙏
補足情報
-
Ethereum (ETH)、BNB Chain (BNB)、Polygon (MATIC)、Avalanche (AVAX)について
上記4つのネットワークは同じウォレットアドレス(0x5CDA2F68f59F641B00aD172475c3d5fC10321174
)を使用します。ただし、送金時には、絶対に使用するネットワーク(例: ERC-20、BEP-20、Polygon、Avalanche C-Chain)を必ず正しく選択してください。 -
USDCやUSDTなどのステーブルコインも、対応するネットワーク経由であれば送金可能です。ただし、送金先のネットワークと選択するネットワークが一致していることを必ず確認してください。
-
初回送金時には少額でテスト送金することをおすすめします。