ガイド

ガイド4 分で読めます

ウェブサイトやアプリにフィードバックフォームを追加する

どのページにもアプリ内にもフィードバックフォームを設置。評価、コメント、送信元ページ、スパム対策、低評価時の通知まで。

Preet SutharIkiform 創業者

1〜5 の評価、任意のコメント、任意のメールアドレスだけの短いフォームを作り、フォームバックエンドに送信すれば、そのためにサーバーを動かす必要はありません。ブラウザーは完全な URL を自分からは教えてくれないので、回答ごとにページのアドレスを送ります。低評価はその日のうちにチームのチャットに流し、すべての回答を 1 か所にまとめて傾向をつかみましょう。

サイトの何が悪いのかを伝えてくれる人はほとんどいません。黙って去っていきます。小さなフィードバックフォームがあれば、伝えてくれる数少ない人の声を、気づいたその瞬間に、原因となったページで拾えます。このガイドでは、どんなサイトやアプリにも合い、送信元のページを送り、ボットを締め出し、不満のある回答をその日のうちにチームに届けるフォームを作ります。

フィードバックフォームの仕組み

フォームは Ikiform に送信します。Ikiform はスコアを検証し、スパムをチェックして保存し、チームにメールを送り、署名付きの Webhook を送ります。Webhook から、低評価はチャットのチャンネルへ、すべての回答はシートやデータベースへ送れます。

1 件のフィードバックがページからチームに届くまで任意のページやアプリ画面評価、コメント、ページのアドレスIkiform検証、スパム除去、保存ダッシュボードフォームと日付で絞り込み、CSV出力通知メールチーム宛て、返信は訪問者へ署名付きWebhookフィールドと meta 付きのsubmission.createdチームのチャットスコア 1 か 2 のみ送信リレー1 件のフィードバックがページからチームに届くまで任意のページやアプリ画面評価、コメント、ページのアドレスIkiform検証、スパム除去、保存ダッシュボードフォームと日付で絞り込み、CSV出力通知メールチーム宛て、返信は訪問者へ署名付き Webhookフィールドと meta 付きのsubmission.createdチームのチャットスコア 1 か 2 のみ送信リレー
  1. 訪問者がスコアを選び、必要ならコメントを書き、フォームがページのアドレスと一緒に送信します。
  2. Ikiform はスコアが 1〜5 の整数であることを確認し、スパムチェックを行って回答を保存します。
  3. 回答はダッシュボードに表示され、絞り込みや CSV でのエクスポートができます。
  4. Ikiform がチームにメールを送ります。訪問者がメールアドレスを残していれば、返信はそのまま本人に届きます。
  5. Ikiform はフィールドとブラウザーのメタデータを含む、署名付きの submission.created Webhook を送ります。
  6. 小さなリレーが低評価だけをチームのチャットに投稿するので、その日のうちに読まれます。
リレーは任意です。まずはメールとダッシュボードから始め、回答がたまってきたらチャット通知を追加しましょう。

フィードバックの集め方

ツールの種類ごとに得意な仕事が違います。ウィジェットの見た目ではなく、回答が届いたときに何を見たいかで選びましょう。

方法得意なことトレードオフ
Hotjar や Usersnap などのウェブサイトフィードバックツールスクリプトタグ 1 つだけで、スクリーンショット、注釈、ページの文脈が取れる。全ページにサードパーティのスクリプトが入り、回答は相手の製品に保存され、料金は相手のプラン次第。
Sentry User Feedback などのエラートラッカーのウィジェットレポートをエラーに結び付け、セッションリプレイを有効にすれば何が起きたかの録画にも結び付く。意見ではなくバグ向け。先にアプリにトラッカーの SDK を入れる必要がある。
Typeform、Tally、Google Forms などのフォームビルダーコードなしで洗練されたフォームが作れる。リンクで送るアンケートに向いている。iframe か別ページになるので、URL で渡さない限り、回答がどのページについてのものかがわからなくなる。
Canny などのフィードバックボード機能リクエストへの公開投票とステータス更新。すばやい反応ではなくアイデア向け。回答は仕様上公開される。
自前の API ルートとデータベースデータとロジックをすべて自分で制御できる。スパム対策、レート制限、通知メール、エクスポートを自分で作る必要があり、サーバーも要る。
Ikiform などのフォームバックエンドどんなサイトやアプリでも自分のマークアップを使える。検証、スパム対策、保存、メール、署名付き Webhook がサーバーなしで手に入る。ファイルアップロードに対応していないのでスクリーンショットは不可。投票やセッションリプレイもない。

すべてのレポートにスクリーンショットが必要なら、フィードバックツールやエラートラッカーのスクリプトを入れる価値があります。フォームをサイトの見た目に合わせ、回答を持ち運べる形で残し、送り先を自分で決めたいなら、フォームバックエンドを使いましょう。このガイドの残りではその方法を説明します。

フォーム

質問は 3 つで十分です。スコア、コメント、任意のメールアドレスです。スコアは誰もが答えてくれる部分、コメントは価値のある部分、メールアドレスはやり取りを完結させる手段です。下でスタックを選んでください。その選択はこれらのガイドのすべてのコードサンプルに引き継がれます。

マークアップ 1 ブロックと短いスクリプトです。JavaScript がなくてもフォームは送信してリダイレクトし、JavaScript があれば訪問者はページにとどまり、サーバーのエラーをボタンの横で確認できます。スクリプトはクエリ文字列を除いたアドレスを page に入れるので、URL 内のトークンが受信トレイに届くことはありません。

feedback.html
<form id="feedback" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <fieldset>
    <legend>How useful was this page?</legend>
    <label><input type="radio" name="rating" value="1" required /> 1</label>
    <label><input type="radio" name="rating" value="2" /> 2</label>
    <label><input type="radio" name="rating" value="3" /> 3</label>
    <label><input type="radio" name="rating" value="4" /> 4</label>
    <label><input type="radio" name="rating" value="5" /> 5</label>
  </fieldset>
  <label for="comment">What would make it better?</label>
  <textarea id="comment" name="comment" rows="4" maxlength="4000"></textarea>
  <label for="feedback-email">Email, if you'd like a reply</label>
  <input id="feedback-email" name="email" type="email" />
  <input type="hidden" name="page" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send feedback</button>
  <p id="feedback-status" role="status"></p>
</form>

<script>
  const form = document.getElementById("feedback");
  const status = document.getElementById("feedback-status");
  form.elements.page.value = location.origin + location.pathname;

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const button = form.querySelector("button");
    button.disabled = true;
    status.textContent = "Sending";
    try {
      const response = await fetch(form.action, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(form),
      });
      const result = await response.json();
      if (result.ok) {
        form.replaceChildren(Object.assign(document.createElement("p"), { textContent: "Thanks. The team reads every note." }));
        return;
      }
      const fields = result.error.fields ?? {};
      status.textContent = fields.rating?.[0] ?? fields.comment?.[0] ?? fields.email?.[0] ?? result.error.message;
    } catch {
      status.textContent = "Couldn't send it. Try again in a moment.";
    }
    button.disabled = false;
  });
</script>
  • frm_xxxxxxxxxxxxxxxx をダッシュボードにある自分のフォームの ID に置き換えます。
  • rating は 5 つの値のラジオグループです。Ikiform は数値として保存するので、平均を出すのに変換は要りません。
  • email は任意です。名乗らなくてよいほうが人は正直に書きますし、返事が欲しい人は自分から残します。
  • 非表示の _gotcha 入力欄がハニーポットです。人には見えません。すべての入力欄を埋めるボットは自らスパムだと示すことになります。
  • page は回答がどのページについてのものかを記録します。自分で送る理由は次のセクションで説明します。
サーバー側の版はシークレットキー付きで送信するので、Ikiform は Allowed domains と人間確認をスキップします。その場合はすべての送信がサーバーの IP から来るため、訪問者ごとに 1 分あたり 5 件という Standard の制限が、全訪問者をまとめて数えることになります。サーバー経由で送信するフォームには Relaxed か Custom のレート制限を選び、IKIFORM_SECRET_KEY はサーバー専用の環境変数に置いてください。

ドキュメントのフレームワーク別ガイドでセットアップを詳しく説明しています。Next.js、Astro、SvelteKit、Nuxt、React、Vue、プレーン HTML をご覧ください。フィードバックフォームのテンプレートは同じフィールドを HTML、React、サーバーアクションとして生成します。長めのフォームはバグ報告とアンケートのテンプレートをどうぞ。

フィールドを定義する

フォームの Fields タブに定義を貼り付けると、Ikiform はどのページからの送信にもそれを適用します。下で試してみてください。プレビューはブラウザー内で Ikiform のバリデーターを実行するので、スコア 0 や不正なメールアドレスでは、実際に返ってくる 422 が表示されます。

プレビュー

Ikiform のバリデーターをブラウザーで実行します。何も送信されません。

このページは役に立ちましたか?

スクリプトが、クエリ文字列を除いたページのアドレスを入れます。

rating は 1 から max(ここでは 5)までの整数を受け付けます。10 まで上げられますが、選択肢は 5 つのほうがすばやく答えられます。
フィールド種類理由
ratingrating、最大 5ワンクリック。数値で保存されるのでページごとに平均を出せる
commenttextarea、最大 4,000 文字スコアの理由
emailemail、任意質問した人に返信できる。通知の reply-to になる
pageurl、任意回答がどのページについてのものか

フィールドを定義すると、送信に含まれるそれ以外の名前は Unknown field として拒否されます。後からアプリのバージョンやプラン名などの文脈も送りたくなったら、先にそのフィールドを追加してください。フィールド ID は小文字の snake_case です。

どのページについての回答かを把握する

ページのわからないスコアでは手の打ちようがありません。Ikiform は送信データごとにブラウザーのメタデータを記録しますが、そのうちの 1 つは見た目ほど役に立ちません。

値取得元得られるもの
meta.user_agentIkiform が記録ブラウザーからの送信なら、ブラウザーと OS
meta.originIkiform が記録サイトのオリジン(https://example.com など)
meta.referrerIkiform が記録たいていはオリジンだけ。ブラウザーの既定は strict-origin-when-cross-origin ポリシーで、別サイトへの送信ではパスが落とされる
meta.countryIkiform が記録2 文字の国コード
pageフォームが送信完全なパス。回答がどのページについてのものかがわかる

だから上のサンプルはすべて page を自分で送っています。HTML 版と SPA 版は location を読み、サーバー版は各フレームワークがすでに知っているアドレスを読みます。どれもクエリ文字列を落とします。そこには保存したくない検索語、トラッキング ID、ワンタイムトークンが入っていることがあるからです。

サーバー版はサーバーから送信するので、meta.user_agent は訪問者のブラウザーではなくサーバーのランタイムになります。ページへのフィードバックならほとんど問題になりませんが、バグ報告では問題になります。バグ報告のガイドでは、ブラウザーのユーザーエージェントをフィールドとして転送しています。

どこで尋ねるか

フォームを置く場所で、届く声が変わります。全ページのフッターに置いたフォームには、強い意見がぽつぽつと届きます。タスクの終わりに置いた質問には、具体的な回答が安定して届きます。

設置場所向いていること注意点
ドキュメントページや記事の末尾疑問に答えられていないページを見つけるページごとの件数は少ない。ページ単位ではなくセクション単位で読む
エクスポートや最初のプロジェクトなど、アプリでのタスクの後特定のフローの使い心地を、記憶が新しいうちにタスクを終えるたびではなく、1 人につき 1 回だけ尋ねる
全画面に浮かぶボタン何でも、いつでも。React ウィジェットを参照自分のボタン、Cookie バナー、チャットの吹き出しを覆ってはいけない
メール内のリンクNPS のように経時的に追いたいスコアメールクライアントは自分のページではないので、フォームにはサイト上のランディングページが必要

どこに置くにしても、フォームは小さく保ちましょう。最初にスコアを尋ね、コメント欄は 2 ステップ目ではなく一緒に表示します。2 ステップ目でほとんどの人が離脱しますし、理由のないスコアでも十分価値があります。

低評価を人の目に届ける

スコアによって回答がどう扱われるか回答を保存すべてのスコア、すべてのページWebhooksubmission.createdリレーが評価を確認3、4、5:それ以上は何もしない週次レビューCSVからページごとの平均チームのチャットスコア、ページ、コメント誰かが返信メールアドレスがあれば署名付きエクスポート1 か 2スコアによって回答がどう扱われるか回答を保存すべてのスコア、すべてのページWebhooksubmission.createdリレーが評価を確認3、4、5:それ以上は何もしない週次レビューCSV からページごとの平均チームのチャットスコア、ページ、コメント誰かが返信メールアドレスがあれば署名付きエクスポート1 か 2
  1. スコアやページに関係なく、すべての回答が保存されます。
  2. Ikiform が署名付きの submission.created Webhook をリレーに送ります。
  3. リレーが評価を読みます。スコア 3〜5 なら、すぐに何かする必要はありません。
  4. スコア 1 か 2 は、ページとコメントと一緒にチームのチャットに送られます。
  5. 訪問者がメールアドレスを残していれば、誰かがその日のうちに返信します。
  6. 週に 1 回、エクスポートでページごとの平均を確認します。傾向はそこに表れます。
通知は悪いスコアだけにしましょう。5 点満点のたびに通知が鳴るチャンネルは、1 週間もしないうちにミュートされます。

通知メールはフォームごとに確認済みの受信者最大 10 人に届き、回答があるたびに送られます。1 日に数件なら問題ありません。それを超えたら、この Next.js ルートのようなリレーで低評価をチャットに送りましょう。同じコードは Cloudflare Worker でも動きます。

app/api/feedback-alerts/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: process.env.IKIFORM_WEBHOOK_SECRET!,
    });
    if (event.type !== "submission.created") return new Response(null, { status: 204 });
    const { rating, comment, page, email } = event.data.fields;
    if (Number(rating) > 2) return new Response(null, { status: 204 });

    const lines = [
      `*${rating}/5* on ${page ?? event.data.meta.origin ?? "an unknown page"}`,
      comment ? `> ${String(comment).slice(0, 500)}` : "_No comment._",
      email ? `Reply to ${email}` : "Anonymous",
    ];
    const response = await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ text: lines.join("\n") }),
    });
    if (!response.ok) return new Response(`Slack answered ${response.status}`, { status: 502 });
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • verifyWebhook が署名とタイムスタンプをチェックするので、他人がこの URL を使ってチャンネルに投稿することはできません。
  • スコアが 2 より大きければ 204 を返して終わります。Ikiform は 2xx ならどれでも配信済みとみなします。
  • Slack の呼び出しが失敗すると 502 を返し、Ikiform が同じ webhook-id でリトライするので、短い障害で通知が失われることはありません。
  • フィールドの値は訪問者が入力したものです。リレーはチャットに届く前にコメントを 500 文字に切り詰めます。

Webhooks でエンドポイントを追加し、その whsec_ シークレットを IKIFORM_WEBHOOK_SECRET にコピーします。ワークスペースごとにエンドポイントは 3 つまでです。ノーコードでつなぐ方法は Slack、Discord、Google Sheets を参照してください。チケットにすべきフィードバックなら、Linear と GitHub Issues に送信データごとにチケットを作るリレーがあります。

ワンクリックの連携はありません。どのツールにも、署名付きの Webhook で直接、または Zapier や n8n などの自動化ツール経由でつなぎます。

ボットを締め出す

全ページのフィードバックフォームは、全ページにある公開フォームです。ボットはそれを見つけ、送ってくるものの大半はリンクです。大部分は Ikiform の既定の設定で対処できます。

防御防げるもの設定
ハニーポットすべての入力欄を埋めるボットサンプルにある非表示の _gotcha 入力欄
スパムスコアリンク 3 つ以上(+2)、既知のスパムフレーズ(+2)、使い捨てメール(+2)、同一内容の繰り返し(+3)。5 に達するとスパムとして保存されるなし
レート制限ループで送信するスクリプトデフォルトで有効: Standard で、訪問者ごとに 1 分あたり 5 件、フォーム全体で 1 分あたり 300 件
許可ドメイン他のサイトからの送信フォームの設定に自分のドメインを追加する
人間確認本物のブラウザーを動かすボットSpam check を Ikiform に設定します。ブラウザーの fetch による送信には challenge() のトークンが必要になります

スパムにも本物の送信と同じ成功の応答を返し、メールも Webhook も送らず、使用量にも数えません。コメントにリンクを 3 つ貼った本物の訪問者が、同じ内容の繰り返しと重なってリンクのルールに引っかかることがあるので、ときどき Spam フィルターを確認してください。回答をスパムではないとマークすると、その時点で Webhook が送られます。各チェックはスパムのドキュメントで説明しています。

回答を読む

最初の数週間はすべてのコメントを読んでください。その後に役立つのは、ページごとの平均スコアを低い順に並べ、横にコメントを置いた表示です。

エクスポート

Submissions を開き、フィードバックフォームを選んで期間を指定し、Export CSV を選びます。列は id、created_at、form、status のあとにフィールドごとに 1 列ずつ並ぶので、上のフィールドなら rating は E 列、page は H 列です。1 回のエクスポートは最大 100,000 行です。それ以上はエクスポートガイドで API を説明しています。

ファイルを Google Sheets にインポートし、空のシートに次の式を入れます。各ページの平均スコアと回答数を、悪い順に一覧にします。

Google Sheets
=QUERY(A2:H, "select H, avg(E), count(E) where D = 'received' and E is not null group by H order by avg(E) asc label avg(E) 'average', count(E) 'answers'", 0)
  1. 回答が 1、2 件しかないページは無視してください。怒った訪問者が 1 人いても傾向とはいえません。
  2. スコアが最も低い 3 ページのコメントを読みます。直すべき点はたいてい最初の数件に書かれています。
  3. ページを変更したら、通算の数字ではなく、変更前と変更後の数週間の平均を比べます。
メールアドレスがなくても、Ikiform に対してフィードバックは匿名ではありません。各回答にはユーザーエージェント、origin、リファラー、国と、外部に返されることのない IP のハッシュが記録されます。収集する内容はプライバシーに関する通知に書いてください。

状況に合わせて選ぶ

上のフォームは、どのページでも使える手軽な反応の収集に対応しています。次のページでは、別の目的を持つフィードバックを扱います。再現すべきバグ、検討すべき要望、追跡すべきスコア、離れていく顧客です。

動作を確認する

すべてのページにフォームを置く前に、ひととおり確認してください。Ikiform に無料プランはありません。テストは無料で、プランのないワークスペースでも localhost からのテスト送信を月 50 件まで受け付けます。本番運用には、月 $6 からで 500 件の Pro を選ぶか、1 回払いで 10,000 件 $49 または 50,000 件 $149 を選びます。クレジットに期限はありません。料金を参照してください。

  1. フォームの Allowed domains に自分のサイトのドメインを追加し、開発中は localhost も追加します。空のままにすると、どこからの送信も受け付けます。
  2. スコアを選び、コメントを書いて送信します。フォームがお礼の一文に置き換わるか、プレーンな HTML 版ではブラウザーがリダイレクト先のページに移動します。
  3. ダッシュボードで Submissions を開きます。回答には rating が数値で、page がフルパスで表示されます。
  4. curl かブラウザーのコンソールでスコア 6 を送信します。Ikiform は rating を示して 422 を返します。
  5. 通知の各受信者の受信トレイを確認します。それぞれにフィールド入りのメールが届き、フォームに email フィールドがあれば Reply で送信者に返信できます。
  6. スコア 1 を送信します。リレーがチームのチャットに投稿し、Webhooks の配信に succeeded と表示されます。
  7. ハニーポットを埋めて _gotcha=bot で送信します。成功の応答は返りますが、送信データはスパムとして保存され、メールも Webhook も送られません。
  8. 何も届かない場合は、ダッシュボードのステータスフィルターを Spam に切り替えてください。スパムにも本物の送信と同じ成功の応答が返ります。

トラブルシューティング

問題対処法
403 origin_not_allowed が返る、またはすべての送信がスパムになるページのドメインがフォームの Allowed domains にありません。ドメインと、ローカルテスト用の localhost を追加してください。登録したドメインのサブドメインは許可されます。
Rating must be at most 5 付きの 422フォームがフィールドの max より高いスコアを送っています。ラジオボタンの値を変えるか、定義の max を最大 10 まで上げてください。
Unknown field の 422フォームが定義にない名前を送っています。新しく追加した hidden 入力欄であることが多いです。その id のフィールドを追加するか、入力欄を削除してください。
page が空値を入れるスクリプトが実行されていないか、フォームができる前に実行されています。スクリプトをフォームの後ろに置くか、SPA のサンプルのように送信時に page を設定してください。
meta.referrer に自分のドメインしか表示されない想定どおりです。ブラウザーはクロスサイトの送信では、デフォルトで origin しか送りません。代わりに page フィールドを使ってください。
送信データはダッシュボードにあるのに、メールも Webhook も届かないスパムとして保存されています。スパムでは何も送られません。Spam フィルターで開いて理由を確認し、スパムではないとマークすると、メールと Webhook が送られます。
429 rate_limited1 人の訪問者がフォームのレート制限を超えて送信しました。Standard では訪問者ごとに 1 分あたり 5 件までです。Retry-After まで待つか、共有の IP 1 つから送信するフォームには Relaxed か Custom を選んでください。
402 test_limit_reachedワークスペースにプランがなく、今月の無料の localhost テスト 50 件を使い切っています。プランを選ぶか、ダッシュボードの Send test を使ってください。

よくある質問

手順で詰まったときや、あなたの状況に合うガイドがほしいときはメールでどうぞ: hello@ikiform.com