# Framer のサイトにウェイトリストを追加する方法

> Framer でウェイトリストの登録を集める 2 つの方法。Ikiform への Webhook を使う Framer のフォームか、訪問者ごとの制限とスパムチェックが残るコードコンポーネントです。

公開日 2026-10-25 · https://ikiform.com/ja/guides/waitlist/framer

Framer のフォームは外部の URL に送信できませんが、各送信データを Webhook に送れるので、ノーコードなら Ikiform のエンドポイントを Webhook にした Framer のフォームを使います。一般公開のローンチには、代わりに小さなコードコンポーネントを使ってください。ブラウザーから直接送信されるので、Ikiform の訪問者ごとのレート制限、許可ドメイン、ハニーポットがすべて機能し続けます。

ページ自体は午後だけで作れるので、ローンチ前のページの多くは Framer で作られています。わかりにくいのはウェイトリストのフォームです。Framer のフォームブロックは `action` を変更できないため、実際のトラフィックが来ると挙動が異なる 2 つの方法から選ぶことになります。

## Framer ならではの違い

- **カスタムの action がありません。** Framer のフォームは、Framer 独自のストレージ、メール、**Webhook** のいずれかの送信先に送ります。コードなしで外部のバックエンドに届ける方法は Webhook だけです。
- **Webhook は訪問者ではなく Framer のサーバーから送られます。** Ikiform に見えるのは Framer の IP アドレスで、`Origin` ヘッダーはありません。そのため、許可ドメイン、訪問者ごとのレート制限、人間確認という 3 つの保護が無効になります。
- **リトライ。** Framer は `2xx` 以外をすべて失敗とみなし、最大 5 回リトライします。リダイレクトはたどりません。Ikiform は JSON の送信に `201` を返すので、保存された登録は 1 回目で成功になります。
- **コードコンポーネントは本物の React です。** 公開サイト上の訪問者のブラウザーで動くので、その中のフォームは他の Web ページと同じように送信します。

図: Framer のウェイトリスト登録が Ikiform に届く 2 つの経路

1. Webhook の経路では、訪問者が Framer のフォームを送信し、Framer のサーバーがフィールドを JSON として Ikiform に転送します。
2. Ikiform からは Framer のサーバーが送信者に見えるので、訪問者を区別したり、フォームがどのサイトにあるかを確認したりできません。
3. コードコンポーネントでは、訪問者のブラウザーが Ikiform に直接送信します。
4. Ikiform には訪問者ごとの IP とあなたのドメインが見えるので、すべての保護が適用されます。

どちらの経路でも保存される登録は同じです。違うのは、Ikiform が誰を送信者とみなすかです。

## 経路を選ぶ

|   | Webhook 付きの Framer フォーム | コードコンポーネント |
| --- | --- | --- |
| 設定 | 右サイドバーの 2 つの項目 | コードファイル 1 つを 1 回貼り付け |
| スタイル | Framer のフォームのスタイルと状態 | 編集できるインラインスタイルか独自のクラス |
| 訪問者ごとのレート制限 | いいえ。すべての送信が Framer の IP を共有 | はい、それぞれ 1 分あたり 5 件 |
| 許可ドメイン | 空のままにする | Framer のドメインと独自ドメインを設定 |
| ハニーポット | いいえ | はい |
| 人間確認 | オフ | オフ（`fetch` で送信するため） |
| 流入元の記録 | 不要 | URL の `?ref=`、またはインスタンスごとの既定値 |

数百人に共有するページなら Webhook の経路で十分です。どこか大きな場所にページを投稿する予定なら、コードコンポーネントを使ってください。Framer のサーバー経由でボットの登録が一気に来ると、止められるのはスパムスコアだけです。

## 経路 1: Webhook 付きの Framer のフォーム

1. ページにフォームを追加し、**Email** の入力欄を 1 つ置きます。入力欄の設定で名前を `email` にします。Framer は各入力欄の名前を JSON のキーとして送り、Ikiform は `email` を返信先と使い捨てドメインのチェックに使います。
2. フォームを選択します。**Send To** の横で **Add…** › **Webhook** を選び、エンドポイント `https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx` を貼り付けます。署名シークレットは任意です。Ikiform は Framer の署名を確認しません。
3. Ikiform では **Allowed domains**（許可ドメイン）を空のままにし、スパムチェックをオフにします。どちらかがオンだと、Framer からの送信には origin がなくブラウザーでの確認も実行できないため、すべての送信がスパム判定されるか拒否されます。
4. [レート制限](https://ikiform.com/ja/docs/rate-limits.md)を Relaxed に、ローンチ当日は Custom に設定します。Standard では Framer からの送信がすべて 1 人の訪問者として数えられ、フォーム全体で 1 分あたり 300 件が上限になります。
5. フォームの成功時の表示を Framer でスタイリングします。Ikiform が `201` を返したときに表示されます。

> フィールド定義があるのにメールアドレスがない場合など、Ikiform が拒否した送信は、Framer の 5 回のリトライでもすべて同じように失敗します。登録が届かなくなったら、Framer で配信を確認してください。

## 経路 2: コードコンポーネント

**Assets** パネルで **Code** を開き、`WaitlistSignup` という名前のコードファイルを作成して、これを貼り付けます。ページにドラッグし、プロパティパネルで **Form ID** を設定します。

`WaitlistSignup.tsx`:

```tsx
import { addPropertyControls, ControlType } from "framer";
import { type CSSProperties, type FormEvent, useRef, useState } from "react";

const box: CSSProperties = {
  font: "inherit",
  padding: "12px 14px",
  borderRadius: 10,
  border: "1px solid rgba(0, 0, 0, 0.15)",
};

interface Props {
  formId: string;
  buttonLabel: string;
  joinedText: string;
  source: string;
}

export default function WaitlistSignup({ formId, buttonLabel, joinedText, source }: Props) {
  const [status, setStatus] = useState<"idle" | "sending" | "joined">("idle");
  const [error, setError] = useState("");
  const key = useRef("");

  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const body = new FormData(event.currentTarget);
    const ref = new URLSearchParams(window.location.search).get("ref");
    body.set("ref", (ref || source).slice(0, 40));
    key.current ||= crypto.randomUUID();
    setStatus("sending");
    setError("");
    try {
      const response = await fetch(`https://ikiform.com/f/${formId}`, {
        method: "POST",
        headers: { Accept: "application/json", "Idempotency-Key": key.current },
        body,
      });
      const result = await response.json();
      if (result.ok) return setStatus("joined");
      setError(result.error.fields?.email?.[0] ?? result.error.message);
    } catch {
      setError("Couldn't reach the server. Try again.");
    }
    setStatus("idle");
  }

  if (status === "joined") return <p role="status" style={{ margin: 0 }}>{joinedText}</p>;

  return (
    <form onSubmit={onSubmit} style={{ display: "flex", flexWrap: "wrap", gap: 8, width: "100%" }}>
      <input
        name="email"
        type="email"
        placeholder="you@company.com"
        aria-label="Email"
        autoComplete="email"
        required
        style={{ ...box, flex: "1 1 220px" }}
      />
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      <button type="submit" disabled={status === "sending"} style={{ ...box, cursor: "pointer" }}>
        {status === "sending" ? "Joining" : buttonLabel}
      </button>
      {error ? <p role="alert" style={{ flexBasis: "100%", margin: 0 }}>{error}</p> : null}
    </form>
  );
}

addPropertyControls(WaitlistSignup, {
  formId: { type: ControlType.String, title: "Form ID", defaultValue: "frm_xxxxxxxxxxxxxxxx" },
  buttonLabel: { type: ControlType.String, title: "Button", defaultValue: "Join the waitlist" },
  joinedText: { type: ControlType.String, title: "Joined text", defaultValue: "You're on the list." },
  source: { type: ControlType.String, title: "Source", defaultValue: "framer" },
});
```

- プロパティパネルには、ボタンのラベル、登録完了時のテキスト、既定の **Source** が表示されます。ヒーローに `hero`、フッターに `footer` のインスタンスを 1 つずつ置けば、どちらがコンバージョンにつながるかがわかります。
- ページの URL の `?ref=` は既定値より優先されるので、キャンペーンのリンクには自動でタグが付きます。
- 冪等性キーは最初の送信時に作られ、同じ訪問中のリトライでは再利用されるので、スマートフォンでダブルタップしても保存される登録は 1 件です。
- `window` は送信ハンドラーの中でだけ読みます。Framer はまずサーバーでコンポーネントをレンダリングするので、レンダリング中に `window` に触れるとそれが壊れます。

### コンポーネント用の Ikiform の設定

メールアドレスを必須にし、`ref` を許可するために、フォームの **Fields** タブで次を追加します:

`フィールド`:

```json
{
  "version": 1,
  "title": "Waitlist",
  "fields": [
    { "id": "email", "type": "email", "label": "Email", "required": true },
    { "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
  ]
}
```

- **Allowed domains** に `your-project.framer.app` と独自ドメインを追加します。`framer.app` だけを追加してはいけません。すべての Framer サイトが一致してしまいます。
- スパムチェックはオフのままにします。コンポーネントは `fetch` で送信し、トークンを取得しないので、チェックがオンのフォームは `403 challenge_required` を返します。
- レート制限は Standard のままにします。これで訪問者がそれぞれ個別に数えられます。

## 動作を確認する

1. サイトを公開します。Webhook もコードコンポーネントも、エディターのプレビューからではなく、公開されたサイトからだけ送信します。
2. `framer.app` ドメインでフォームを送信し、独自ドメインでももう一度送信します。
3. ダッシュボードで **Submissions** を開きます。送ったすべてのフィールドを含む新しい送信データがあります。
4. コンポーネントの場合は、`?ref=test` 付きでページを開き、登録の `ref` が `test` になっていることを確認します。
5. 通知の受信者それぞれの受信箱を確認します。各自にフィールドを載せたメールが届き、フォームに `email` フィールドがあれば **Reply** は送信者宛てになります。
6. 何も届かない場合は、ダッシュボードのステータスフィルターを **Spam** に切り替えてください。スパムにも本物の送信データと同じ成功の応答が返ります。

## ハマりどころ

| 問題 | 対処法 |
| --- | --- |
| Webhook 経由の登録がすべて `origin_mismatch` でスパムに入る | **Allowed domains** が設定されていますが、Framer のサーバーからの送信には origin がありません。このフォームでは設定を空にするか、コードコンポーネントに切り替えてください。 |
| Framer が Webhook の配信失敗を報告する | Ikiform が送信を拒否しています。スパムチェックがオンで `403 challenge_required` になっているか、入力欄の名前が `email` でないため `422` になっています。原因を直してください。リトライしても同じように失敗します。 |
| `429 rate_limited` | 1 人の訪問者が、フォームのレート制限を超えて送信しました。Standard では訪問者ごとに 1 分あたり 5 件までです。`Retry-After` まで待つか、共有の 1 つの IP から送信するフォームには Relaxed か Custom を選んでください。 |
| コンポーネントに `Unknown field "ref"` と表示される | フィールド定義に `ref` がありません。上の定義のように追加してください。 |
| `403 origin_not_allowed` が返るか、すべての送信がスパムになる | ページのドメインがフォームの **Allowed domains** に入っていません。ドメインと、ローカルテスト用に `localhost` を追加してください。登録したドメインのサブドメインも許可されます。 |

お問い合わせフォーム版を含む Framer の設定の全体は、[Framer のドキュメントガイド](https://ikiform.com/ja/docs/guides/framer.md)にあります。リストのエクスポートとローンチメールの送信は[メインガイド](https://ikiform.com/ja/guides/waitlist.md#launch-day)で説明しています。

## よくある質問

### Framer のフォームは外部の URL に送信できますか？

フォームの action としてはできません。Framer のフォームは Framer 独自の送信先に送り、その 1 つが任意の https URL への Webhook です。Ikiform にはそれを使うか、コードコンポーネントで独自のフォームをレンダリングしてください。

### Framer のウェイトリスト登録がスパムになるのはなぜですか？

Webhook の経路では、Framer は Origin ヘッダーなしで自社のサーバーから送信するので、Allowed domains を設定したフォームではすべての送信が origin_mismatch になります。Webhook を使うフォームでは Allowed domains を空にしてください。

### Framer の Webhook 経由の登録は Framer のフォーム上限に数えられますか？

Framer はフォームの送信数を無制限としているので、Webhook 経由の登録が Framer の上限に達することはありません。コードコンポーネントの経路では Framer のフォームをまったく使いません。

### Ikiform は無料ですか？

いいえ、無料プランはありません。テストは無料で、支払う前に localhost からテスト送信を月 50 件まで送れます。本番運用には、500 件で月 $6 からの Pro か、買い切りで 10,000 件 $49、または 50,000 件 $149 を選べます。クレジットに有効期限はありません。

## その他のガイド

- [新しい SaaS のウェイトリスト](https://ikiform.com/ja/guides/waitlist.md): 自分のランディングページにローンチ前のウェイトリストを作ります。フォーム、スパム対策、重複、確認メール、ローンチ日のきれいなエクスポートまで扱います。
- [Webflow のウェイトリスト](https://ikiform.com/ja/guides/waitlist/webflow.md): Webflow の Form Block の送信先を Ikiform にし、非表示フィールドで登録に流入元のタグを付け、短いスクリプトですべてのフォームで Webflow の成功表示を残します。
- [Carrd のウェイトリスト](https://ikiform.com/ja/guides/waitlist/carrd.md): Carrd のページでウェイトリストの登録を集める方法。Pro Plus では UTM を記録する Send to URL フォーム、Pro Standard ではハニーポット付きの埋め込み HTML フォームを使います。
- [プレーンな HTML のウェイトリスト](https://ikiform.com/ja/guides/waitlist/html.md): ビルド不要でプレーンな HTML だけのウェイトリストページ。JavaScript がオフでも動き、読み込めばその場でお礼を表示し、必要なときはフォールバックします。
- [Product Hunt ローンチリスト](https://ikiform.com/ja/guides/waitlist/product-hunt.md): Product Hunt の数週間前からローンチリストを集め、登録をチャネルごとにタグ付けし、太平洋時間のリセットに合わせてローンチ日のメールを 2 回に分けて送ります。
- [選考付きのベータ登録](https://ikiform.com/ja/guides/waitlist/beta-signup.md): サーバーが必須を強制する選考質問付きのベータ応募フォームと、応募者を採点してリストから各コホートを選ぶスクリプトを紹介します。
