フォーム ID

Nuxt

ハイドレーション前はプレーンなフォームとして、後は $fetch で送信する Nuxt のコンポーネント。フォーム ID はランタイム設定に置きます。nuxt generate でも動きます。

Nuxt はフォームをサーバーで描画するので、ブラウザーに届く HTML はすでに動くフォームです。アプリがハイドレーションされるまでは通常の HTML フォームとして送信し、その後はコンポーネントが引き継いで結果をその場に表示します。サーバーでは何も実行しないので、nuxt generate でビルドした静的サイトでも動きます。

代わりにシークレットキーを使って Nuxt のサーバールートから送信するには、Vue を参照してください。

セットアップ

  1. フォーム ID をランタイム設定に置く

    nuxt.config.ts
    export default defineNuxtConfig({
      runtimeConfig: {
        public: {
          ikiformFormId: "frm_xxxxxxxxxxxxxxxx",
        },
      },
    });

    環境ごとに NUXT_PUBLIC_IKIFORM_FORM_ID で上書きできます。nuxt generate では値がビルド時に埋め込まれるので、ビルド環境で変数を設定してください。

  2. コンポーネントを追加する

    app/components/ContactForm.vue
    <script setup lang="ts">
    type Result =
      | { ok: true; id: string }
      | { ok: false; error: { message: string; fields?: Record<string, string[]> } };
    
    const config = useRuntimeConfig();
    const endpoint = `https://ikiform.com/f/${config.public.ikiformFormId}`;
    
    const status = ref<"idle" | "sending" | "sent" | "error">("idle");
    const errors = ref<Record<string, string[]>>({});
    const message = ref("");
    
    async function onSubmit(event: Event) {
      status.value = "sending";
      const result = await $fetch<Result>(endpoint, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(event.currentTarget as HTMLFormElement),
        ignoreResponseError: true,
      }).catch(() => null);
      if (result?.ok) {
        status.value = "sent";
        return;
      }
      errors.value = result?.error.fields ?? {};
      message.value = result?.error.message ?? "Couldn't reach the server. Try again.";
      status.value = "error";
    }
    </script>
    
    <template>
      <p v-if="status === 'sent'">Thanks, we got it.</p>
      <form v-else :action="endpoint" method="post" @submit.prevent="onSubmit">
        <label>
          Email
          <input name="email" type="email" autocomplete="email" required />
        </label>
        <p v-if="errors.email">{{ errors.email[0] }}</p>
        <label>
          Message
          <textarea name="message" rows="5" />
        </label>
        <input type="hidden" name="_redirect" value="https://example.com/thanks" />
        <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
        <p v-if="status === 'error' && !errors.email" role="alert">{{ message }}</p>
        <button :disabled="status === 'sending'">{{ status === "sending" ? "Sending" : "Send" }}</button>
      </form>
    </template>
    • Nuxt 3 では、app/components/ ではなく components/ に置きます。
    • ignoreResponseError: true を付けると $fetch が 4xx で例外を投げなくなり、エラーの情報がデータとして返ります。catch に届くのはネットワークエラーだけです。
    • @submit.prevent はアプリがハイドレーションされて初めて有効になります。それまでは、ブラウザーが action に送信して _redirect に従います。
  3. ページで使う

    app/pages/contact.vue
    <template>
      <ContactForm />
    </template>

このフォールバックにはサーバーレンダリングかプリレンダリングが必要です。ssr: false では JavaScript が動くまでページが空なので、JavaScript なしで送信できるフォームがありません。

許可ドメイン

本番に加えて、デプロイ先のプレビューのホストを登録します。

フォームが動く場所許可ドメインに追加するもの
nuxt devlocalhost。http://localhost:3000 に一致します。Ikiform が比較するのはホスト名だけで、ポートは比較しません。
自分のドメインexample.com(www.example.com も含まれます)
Vercelyour-project.vercel.app。your-project-git-branch-team.vercel.app のようなプレビュー URL は一致しないので、テストする間はプレビューの正確なホストを追加してください。
Netlifyyour-site.netlify.app。デプロイプレビュー(deploy-preview-12--your-site.netlify.app)やブランチデプロイ(branch--your-site.netlify.app)はそのサブドメインではないので、テストする間はプレビューの正確なホストを追加してください。
Cloudflare Pagesyour-project.pages.dev。abc123.your-project.pages.dev のようなプレビュー URL はそのサブドメインなので、すでに一致します。
  • vercel.app、netlify.app、pages.dev だけを追加しないでください。他の人のサイトも含め、そのホスト上のすべてのサイトが一致してしまいます。

テストのチェックリスト

  1. 許可ドメインに localhost を入れて nuxt dev を実行し、送信します。お礼のメッセージがフォームに置き換わるはずです。
  2. ブラウザーの開発者ツールで JavaScript をオフにし、再読み込みして送信します。ローカルでは Ikiform のサンクスページに、https: のドメインでは /thanks に移動するはずです。
  3. フィールドを定義した状態で無効なメールアドレスを送信し、メッセージがフィールドの下に表示されることを確認します。
  4. nuxt generate を実行して出力をデプロイし、プレビューのホストから送信します。

トラブルシューティング

症状原因対処
FetchError: [POST] "https://ikiform.com/f/...": 422ignoreResponseError がないため、$fetch がエラーで例外を投げるignoreResponseError: true を追加します。
404 not_found になり、action が /f/undefined で終わっているサイトのビルド時にフォーム ID が設定されていなかったビルド環境で NUXT_PUBLIC_IKIFORM_FORM_ID を設定するか、nuxt.config.ts でデフォルト値を設定します。
JavaScript なしでは何も起きないページが ssr: false でクライアントレンダリングされているサーバーレンダリングを有効にするか、ページをプリレンダリングします。
送信データが origin_mismatch でスパムフィルターの下に届く送信したページが、フォームの許可ドメインに含まれていない上の表のホストを追加します。Reject を選んでいる場合、同じ送信には代わりに 403 origin_not_allowed が返ります。