フォーム ID

SvelteKit

フォームを Ikiform に直接送信し、JavaScript が読み込まれると fetch で拡張される、プリレンダリングされた SvelteKit のページ。adapter-static でも動きます。

このページにサーバーは不要です。フォームの action は Ikiform のエンドポイントなので、adapter-static やどの静的ホストでも動きます。JavaScript がなければプレーンな HTML のように送信してリダイレクトし、JavaScript があれば fetch で送信して結果をその場に表示します。代わりにシークレットキーを使ってフォームアクション経由で送信するには、Svelte を参照してください。

use:enhance を使わない理由

SvelteKit の use:enhance が動くのは、+page.server.js で定義したアクションに向けた method="POST" のフォームだけです。Ikiform は外部の URL なので、このページでは代わりに小さな onsubmit ハンドラーを使います。プログレッシブエンハンスメントの考え方は同じで、まずフォームが動き、スクリプトがそれを改善します。

セットアップ

  1. 公開環境変数を追加する

    .env
    PUBLIC_IKIFORM_FORM_ID=frm_xxxxxxxxxxxxxxxx
    PUBLIC_SITE_URL=https://example.com

    PUBLIC_SITE_URL は JavaScript なしの場合のリダイレクト用です。プリレンダリング中の page.url.origin は自分のドメインではなく http://sveltekit-prerender なので、そこからリダイレクトを組み立てることはできません。

  2. ページをプリレンダリングする

    src/routes/contact/+page.js
    export const prerender = true;
  3. フォームを追加する

    src/routes/contact/+page.svelte
    <script>
      import { PUBLIC_IKIFORM_FORM_ID, PUBLIC_SITE_URL } from "$env/static/public";
    
      const endpoint = `https://ikiform.com/f/${PUBLIC_IKIFORM_FORM_ID}`;
    
      let status = $state("idle");
      let errors = $state({});
      let message = $state("");
    
      async function onsubmit(event) {
        event.preventDefault();
        status = "sending";
        try {
          const response = await fetch(endpoint, {
            method: "POST",
            headers: { Accept: "application/json" },
            body: new FormData(event.currentTarget),
          });
          const result = await response.json();
          if (result.ok) {
            status = "sent";
            return;
          }
          errors = result.error.fields ?? {};
          message = result.error.message;
        } catch {
          message = "Couldn't reach the server. Try again.";
        }
        status = "error";
      }
    </script>
    
    {#if status === "sent"}
      <p>Thanks, we got it.</p>
    {:else}
      <form action={endpoint} method="POST" {onsubmit}>
        <label>
          Email
          <input name="email" type="email" autocomplete="email" required aria-invalid={errors.email ? "true" : undefined} />
        </label>
        {#if errors.email}<p>{errors.email[0]}</p>{/if}
        <label>
          Message
          <textarea name="message" rows="5"></textarea>
        </label>
        <input type="hidden" name="_redirect" value={`${PUBLIC_SITE_URL}/contact/thanks`} />
        <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
        {#if status === "error" && !errors.email}<p role="alert">{message}</p>{/if}
        <button disabled={status === "sending"}>{status === "sending" ? "Sending" : "Send"}</button>
      </form>
    {/if}

    リダイレクト先として src/routes/contact/thanks/+page.svelte を追加します。

SvelteKit の CSP を使う場合

kit.csp を設定している場合は、fetch の送信とプレーンなフォームの送信の両方で Ikiform を許可してください。そうしないと、ブラウザーがどちらかをブロックします。

svelte.config.js
const config = {
  kit: {
    csp: {
      directives: {
        "connect-src": ["self", "https://ikiform.com"],
        "form-action": ["self", "https://ikiform.com"],
      },
    },
  },
};

export default config;

許可ドメイン

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

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

テストのチェックリスト

  1. 許可ドメインに localhost を入れて開発サーバーを実行し、送信します。お礼のメッセージがフォームに置き換わるはずです。
  2. vite build を実行し、出力の contact.html にすでにフォームが含まれていることを確認します。
  3. JavaScript をオフにし、再読み込みして送信します。https: のドメインでは /contact/thanks に移動するはずです。
  4. プレビューのデプロイから送信し、スパムになっていないことを確認します。

トラブルシューティング

症状原因対処
PUBLIC_IKIFORM_FORM_ID が $env/static/public からエクスポートされていないというエラーでビルドが失敗するビルドを実行する環境で変数が設定されていないローカルでは .env に、ホスティング先ではビルド設定に追加します。
送信しても何も起きず、コンソールに x-sveltekit-action ヘッダーに関する CORS エラーが表示されるアプリの外に送信するフォームに use:enhance が付いているそれを削除し、onsubmit ハンドラーを残します。
form-action または connect-src のせいで、ブラウザーがフォームの送信を拒否するkit.csp に Ikiform が含まれていない両方のディレクティブに https://ikiform.com を追加します。
JavaScript なしだと Ikiform のサンクスページに移動するPUBLIC_SITE_URL が http:、またはドメインが許可ドメインにないhttps: のアドレスを使い、ドメインを追加します。
送信データが origin_mismatch でスパムフィルターの下に届く送信したページが、フォームの許可ドメインに含まれていない上の表のホストを追加します。Reject を選んでいる場合、同じ送信には代わりに 403 origin_not_allowed が返ります。