この記事で扱う内容
Next.js×MicroCMS×HubSpotで制作していたサイトにお問い合わせフォームを設計・実装する中で、思った以上に検討すべきポイントが多く、非常に考えさせられる部分が多いなと感じました。
そこで今回は、実際に工夫した点や検討したポイントについてまとめてみました。
設計方針と実装のこだわりポイント
1. 埋め込みフォームではなく Server Actions を採用
(設計方針)
今回、単なるHubSpotの埋め込みフォームではなく、Next.jsのServer Actionsを使ってサーバーサイドでフォーム送信処理を完結させました。
✅今回採用した設計
Reactフォーム
↓
Next.js Server Action
↓
HubSpot Forms API
✅埋め込みによる設計
Embed Form
↓
HubSpotが生成したフォーム
↓
HubSpot CRM
今回は自作UIなので以下のメリットがありました
- UIを完全に自由に作れるので、UI/UXを重視出来る
- バリデーション・状態管理を自分でコントロール出来る
デメリットもあります
- Cookie(hubspotutk)を自分で扱う必要がある
- 仕様変更に弱い
- 実装コストがやや高い
(実装ポイント)
バリデーションルールは以下のように実装致しました。
function validateEmail(email: string) {
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email);
}
if (!rawFormDate.name) {
return {
status: "error",
message: "お名前は必須項目です。",
};
}
if (!rawFormDate.email) {
return {
status: "error",
message: "メールアドレスは必須項目です。",
};
}
if (!validateEmail(rawFormDate.email)) {
return {
status: "error",
message: "メールアドレスの形式が誤っています。",
};
}
if (!rawFormDate.message) {
return {
status: "error",
message: "お問い合わせ内容を入力してください。",
};
}
return {
status: "success",
message: "お問い合わせを送信しました。内容確認後、ご連絡致します。",
};
2. レイアウト分割でスクリプトの読み込み範囲を限定
(設計方針)
HubSpotのスクリプトはコンタクトフォームのページにのみ読み込むようにレイアウトを分割しました。これにより、全体のパフォーマンス向上と不要なスクリプトの読み込みを防ぎ、運用効率も高める狙いです。
(実装ポイント)
Next.jsのルートレイアウト以外で<head>を直接使うと、HTML構造が重複・不整合になってしまうため、next/scriptの strategy="afterInteractive"を活用し、コンタクトページ専用レイアウト内にスクリプトを埋め込みました。
<Script
id="hubspot-tracking"
strategy="afterInteractive"
src={`https://js.hs-scripts.com/${process.env.NEXT_PUBLIC_HUBSPOT_PORTAL_ID}.js`}
/>
👉Next.jsが内部で正しく<head>へ挿入してくれます。
3. クライアントの Cookieを取得しサーバーに送信
(設計方針)
ユーザーのHubSpotトラッキングCookie(hubspotutk)をクライアント側で取得し、フォーム送信時にサーバーに渡すことで、フォーム送信を既存のユーザー行動データと正しく紐付けられるようにしました。
(実装ポイント)
1. クライアントサイドでCookieを取得し、hiddenフィールドにセット
フォームコンポーネント内(クライアントサイド)で、JavaScriptを使ってdocument.cookieからhubspotutkを抽出します。具体的には、document.cookieをパースし、hubspotutkが含まれるか確認し、その値を取得します。
<input
type="hidden"
name="hutk"
value={ typeof document !== "undefined"
? document.cookie
.split("; ")
.find((row) => row.startsWith("hubspotutk="))
?.split("=")[1] ?? "" : "" }
/>
document.cookie.split("; ")でクッキーの一覧を分割して、
.find(row => row.startsWith("hubspotutk="))でhubspotutkを探し、
.split("=")[1]で値だけを取り出しています。
2. サーバー側で受け取り、HubSpot API送信時のcontext.hutkに含める
フォームの送信時に、取得したhubspotutkをフォームデータに含めてサーバーへ送信します。
これにより、サーバー側で受け取ったデータと一緒にhubspotutkをHubSpot APIへ渡すことができます。
これにより、フォーム送信データがユーザーの行動履歴と正しく紐付けられ、精度の高い分析が可能になります。
const payload = {
fields: [
{
name: "fullname",
value: rawFormDate.name,
},
{
name: "company",
value: rawFormDate.company,
},
{
name: "email",
value: rawFormDate.email,
},
{
name: "message",
value: rawFormDate.message,
},
],
context: {
hutk: rawFormDate.hutk,
pageUri: process.env.NEXT_PUBLIC_SITE_URL + "/contact",
pageName: "Contact",
},
};
body: JSON.stringify(payload),
4. ユーザーの IPアドレス取得を実装
(設計方針)
フォームアナリティクスの精度向上を狙い、Next.jsのheaders() APIを使って送信元IPアドレスを取得。これをHubSpotのAPIに渡すことで、解析に活用できるようにしました。
(実装ポイント)
import { headers } from "next/headers";
type FormState = {
...
formData: FormData
): Promise<FormState> {
const headersList = await headers();
const ip =
headersList.get("x-forwarded-for")?.split(",")[0] ??
headersList.get("x-real-ip") ??
"";
const rawFormDate = {
...
ipAddress: ip,
};
5. HubSpotに合わせた 新規プロパティ(fullname) の作成
HubSpotの標準プロパティは「firstname」「lastname」などに分かれています。今回は1つでフルネームを管理したかったので、「fullname」というプカスタムプロパティをHubSpot側に新規作成し、送信データをマッピングしました。
fields: [
{
name: "fullname",
value: rawFormDate.name,
},
// その他のフィールド
],
6. セキュリティ面での考慮
サーバーサイドでのバリデーション実施
クライアント側のバリデーションはUX向上に役立ちますが、改ざんリスクがあるため、最終的なバリデーションは必ずサーバーサイド(Server Actions)で実行しています。これにより、不正データの送信や意図しないAPI呼び出しを防止し、セキュリティを高めています。
環境変数による機密情報管理
HubSpotのポータルIDやフォームIDなどの機密情報は、.envファイルやVercelのSecret管理機能を使って環境変数として管理し、ソースコードに直接書かないようにしています。これにより、リポジトリの漏洩や公開による情報流出のリスクを軽減しています。
まとめ
今回のお問い合わせフォーム実装では、Next.jsのServer Actionsを活用し、HubSpotのAPIと連携することで、UI/UXに配慮した柔軟かつ堅牢な設計を実現しました。
- 埋め込みフォームに頼らず自前で状態管理やバリデーションを制御
- レイアウト分割で必要ページにのみスクリプトを読み込み、パフォーマンス最適化
- クライアント側でCookieを取得しサーバーに送信、既存ユーザーデータとの紐付けを強化
- IPアドレスの取得を実装し、フォームアナリティクスの精度向上に寄与
ただし、現状HubSpot側でIPアドレスがフォーム解析データに反映されていない点があります。これは実装上の問題ではなく、HubSpot側の仕様や解析処理に起因する可能性が高いです。今後のアップデートやHubSpotの設定変更により対応可能になることを期待しています。