Nuxt
ハイドレーション前はプレーンなフォームとして、後は $fetch で送信する Nuxt のコンポーネント。フォーム ID はランタイム設定に置きます。nuxt generate でも動きます。
Nuxt はフォームをサーバーで描画するので、ブラウザーに届く HTML はすでに動くフォームです。アプリがハイドレーションされるまでは通常の HTML フォームとして送信し、その後はコンポーネントが引き継いで結果をその場に表示します。サーバーでは何も実行しないので、nuxt generate でビルドした静的サイトでも動きます。
代わりにシークレットキーを使って Nuxt のサーバールートから送信するには、Vue を参照してください。
セットアップ
フォーム ID をランタイム設定に置く
nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { public: { ikiformFormId: "frm_xxxxxxxxxxxxxxxx", }, }, });環境ごとに
NUXT_PUBLIC_IKIFORM_FORM_IDで上書きできます。nuxt generateでは値がビルド時に埋め込まれるので、ビルド環境で変数を設定してください。コンポーネントを追加する
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に従います。
- Nuxt 3 では、
ページで使う
app/pages/contact.vue <template> <ContactForm /> </template>
このフォールバックにはサーバーレンダリングかプリレンダリングが必要です。ssr: false では JavaScript が動くまでページが空なので、JavaScript なしで送信できるフォームがありません。
許可ドメイン
本番に加えて、デプロイ先のプレビューのホストを登録します。
| フォームが動く場所 | 許可ドメインに追加するもの |
|---|---|
nuxt dev | localhost。http://localhost:3000 に一致します。Ikiform が比較するのはホスト名だけで、ポートは比較しません。 |
| 自分のドメイン | example.com(www.example.com も含まれます) |
| Vercel | your-project.vercel.app。your-project-git-branch-team.vercel.app のようなプレビュー URL は一致しないので、テストする間はプレビューの正確なホストを追加してください。 |
| Netlify | your-site.netlify.app。デプロイプレビュー(deploy-preview-12--your-site.netlify.app)やブランチデプロイ(branch--your-site.netlify.app)はそのサブドメインではないので、テストする間はプレビューの正確なホストを追加してください。 |
| Cloudflare Pages | your-project.pages.dev。abc123.your-project.pages.dev のようなプレビュー URL はそのサブドメインなので、すでに一致します。 |
vercel.app、netlify.app、pages.devだけを追加しないでください。他の人のサイトも含め、そのホスト上のすべてのサイトが一致してしまいます。
テストのチェックリスト
- 許可ドメインに
localhostを入れてnuxt devを実行し、送信します。お礼のメッセージがフォームに置き換わるはずです。 - ブラウザーの開発者ツールで JavaScript をオフにし、再読み込みして送信します。ローカルでは Ikiform のサンクスページに、
https:のドメインでは/thanksに移動するはずです。 - フィールドを定義した状態で無効なメールアドレスを送信し、メッセージがフィールドの下に表示されることを確認します。
nuxt generateを実行して出力をデプロイし、プレビューのホストから送信します。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
FetchError: [POST] "https://ikiform.com/f/...": 422 | ignoreResponseError がないため、$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 が返ります。 |