Next.js / Vercel / HubSpot Forms APIの組み合わせでお問い合わせフォームを作ってみた

静的サイト(Jamstack含む)にお問い合わせフォームをつけたいという要望はとても多いです。私のWebサイトにも念のためフォームを設置しています。せっかくなので、顧客管理ができるCRMのHubSpotのForm APIと連携して、フォームのデータがそのままCRMに入るようにしてみました。

私は個人事業主でほぼ紹介のみで仕事を得ているため(ありがとうございます🙏)、営業することもないのですが、顧客のためにも勉強したく、HubSpot CRMを無料の範囲で試しています。それぞれのCRMで得意なこと、できることが異なりますが、競合のSと比べて設定が手軽なことや、Zと比べるとローカライズやサポートがよい点で、たいへん好印象です😊

目次

HubSpot APIについて

HubSpotはCRMとデータを連携するためのAPIを公開しています。HubSpotの契約内容によって、使えるAPIや利用上限が異なっています。Forms APIは、無料版のHubSpotでも利用でき、10秒につき100リクエストの制限がありますが、小規模なサイトであれば充分にまかなえる範囲でしょう。


API by Tiresより引用

Forms API は今ちょうどリニューアル中でbeta版のようなので、今回はstable版である v3のAPIを利用します。
無料のDeveloperアカウントを作成すれば、Sandboxとして使えるテストアカウントも作成できるようになります。

フォームを作成する

まずはHubSpotでフォームを作成します。アカウント種別によってナビゲーションが異なるようですが、無料アカウントでは[マーケティング]→[フォーム]からフォームを作成できました。
今回は[埋め込みフォーム]を選択し、「お問い合わせ」テンプレートを選択して「開始」ボタンを押します。






必要であれば、適宜、項目名や必須項目の扱いなどを変更していきます。
[更新]→[公開]ボタンをクリックすると、フォーム埋め込みコードのモーダルが開きます。



この画面のportalIdとformIdを後ほど使用しますので、値をメモしておきます。

フォームを受信トレイと連携する

HubSpotでは従来のメールフォーム同様にメール通知も送信できますが、受信トレイにて独立した「フォーム」エリアにも内容を表示できます。フォームで送信された内容に気づきやすくなりますので、この連携も設定しておきます。

画面上部の歯車アイコンから、受信トレイ設定を開きます。

チャネル欄にある「チャネルを接続」ボタンを押します。
どのチャネルを接続しますか?と尋ねられますので「フォーム」を選択します。


フォームを受信トレイに接続する画面が開きますので、先ほど作成したフォームを選択して、右下の「次へ」ボタンを押します。

これでフォームを受信トレイに接続できました!
※もしメール通知が欲しい場合は、フォームの編集画面の「オプション」の「次の宛先に送信通知を送る」からメールアドレスを設定できます。

フォームをページ内で表示する

いよいよ、フォームをページ内で表示します。
フォーム作成にあたり、下記の記事を参考にしました。英語ですが各項目の説明もありますので、ぜひ参考にしてください。


かなりの部分がそのままですが、下記にコードを貼り付けます。
オリジナルとの違いは、項目名の違い、HTMLにTailwindのクラスを付与していること、フォーム送信後に送信完了画面に転送していることです。
メモしてある portalId と formGuid の値をコード内で置き換えてください。あとは、適宜、React Hook Formなどでイケてるバリデーションを加えたり、フォーム送信完了・失敗の処理を入れてから使用してください(React歴3ヶ月の私は、ここで力尽きました…あとで頑張ろう💪)

import { useState, VFC } from "react";
import axios from 'axios';
import { useRouter } from 'next/router'

export const Component: VFC = () => {
  const router = useRouter();
  
  // 項目を設定
  const [lastname, setLastname] = useState("");
  const [firstname, setFirstname] = useState("");
  const [email, setEmail] = useState("");
  const [message, setMessage] = useState("");

  const handleSubmit = async (e) => {
    // デフォルトの動作をブロック
    e.preventDefault();
    
    // フォーム送信へ
    await submit_hubspot_form(firstname, lastname, email, message);
  }

  const submit_hubspot_form = async (firstname, lastname, email, message) => {
    const portalId = 'ポータルID';
    const formGuid = 'フォームGUIDを記入';
    const config = {
      headers: {
        'Message-Type': 'application/json',
      },
    }

    return await axios.post(`https://api.hsforms.com/submissions/v3/integration/submit/${portalId}/${formGuid}`,
      {
        portalId,
        formGuid,
        fields: [
          {
            name: 'firstname',
            value: firstname,
          },
          {
            name: 'lastname',
            value: lastname,
          },
          {
            name: 'email',
            value: email,
          },
          {
            name: 'message',
            value: message,
          },
        ],
      },

      config
    ).then(() => {
      // フォーム送信後にリダイレクトする
      router.push('/')
    });
  }

  return (
    <form className={'mt-4'} onSubmit={handleSubmit}>
      <fieldset>
        <legend className={'block text-gray-700 text-sm font-bold mb-2'}>お名前</legend>
        <div className={'flex gap-2'}>
          <div className={'flex-1'}>
            <label className={'block text-gray-700 text-sm font-bold mb-2'} htmlFor="lastname">姓 [必須]</label>
            <input
              className={'appearance-none border border-gray-500 rounded w-full py-2 px-3'}
              id="lastname"
              name="lastname"
              type="text"
              value={lastname}
              placeholder="佐藤"
              onChange={e => setLastname(e.target.value)}
              required/>
          </div>
          <div className={'flex-1'}>
            <label className={'block text-gray-700 text-sm font-bold mb-2'} htmlFor="firstname">名</label>
            <input
              className={'appearance-none border border-gray-500 rounded w-full py-2 px-3'}
              id="firstname"
              name="firstname"
              type="text"
              value={firstname}
              placeholder="あゆみ"
              onChange={e => setFirstname(e.target.value)}
            />
          </div>
        </div>
      </fieldset>
      <div className={'mt-4'}>
        <label className={'block text-gray-700 text-sm font-bold mb-2'} htmlFor="email">メールアドレス [必須]</label>
        <input
          className={'appearance-none border border-gray-500 rounded w-full py-2 px-3'}
          id="email"
          name="メールアドレス"
          placeholder="your@email.address"
          type="email"
          value={email}
          onChange={e => setEmail(e.target.value)}
          required/>
      </div>
      <div className={'mt-4'}>
        <label className={'block text-gray-700 text-sm font-bold mb-2'} htmlFor="message">お問い合わせ [必須]</label>
        <textarea
          className={'h-48 appearance-none border border-gray-500 rounded w-full py-2 px-3'}
          id="message"
          name="message"
          value={message}
          onChange={e => setMessage(e.target.value)}
          required/>
      </div>
      <button className={'mt-6 bg-indigo-800 hover:bg-indigo-700 text-white font-bold py-2 px-8 rounded focus:outline-none focus:shadow-outline'} type="submit">送信する</button>
    </form>
  );
};

こうなります


フォームからお問い合わせを送信すると、受信トレイの「フォーム」にて確認できます。
フォームから入力した氏名がデータベースに反映されており、過去に同じメールアドレスでやりとりしている場合はきちんとその情報も紐づいて表示されます👾✨
なんて便利なんでしょう🥺 もうHubSpotなしの生活には戻れませんね!

メール通知を設定している場合は、下記のようなメールが届きます。


ここでお気づきの方もいらっしゃると思いますが、どのページのフォームから送信されたなどをオプションとして送信データに含めることができます。また、送信時にうまいことすることでトラッキングも有効にできるそうです。
ドキュメントを見て実装してください💪

※公開後に気づきましたが、Vercelあんまり関係なかったですね…

女性が笑っている
佐藤あゆみ(旧姓:古庄)
屋号Pentaprogram フリーランス Web制作者/著書「Webページ高速化超入門 」/計算メモ帳アプリ「Kanau」制作

関連記事

  1. 雑記
  2. Next.js / Vercel / HubSpot Forms APIの組み合わせでお問い合わせフォームを作ってみた