# Comment ajouter une liste d'attente à un site Framer

> Deux façons de collecter des inscriptions sur Framer : le formulaire Framer avec un webhook vers Ikiform, ou un code component qui garde limites et anti-spam.

Publié le 2026-10-25 · https://ikiform.com/fr/guides/waitlist/framer

Les formulaires Framer ne peuvent pas envoyer vers une URL externe, mais ils peuvent envoyer chaque soumission à un webhook : la voie sans code est donc le formulaire Framer avec votre endpoint Ikiform comme webhook. Pour un lancement public, utilisez plutôt un petit code component : le navigateur envoie directement, donc la limite de débit par visiteur, les domaines autorisés et le honeypot d'Ikiform continuent tous de fonctionner.

Framer est l'outil de nombreuses pages d'avant-lancement, car la page elle-même se fait en un après-midi. C'est le formulaire de liste d'attente qui est moins évident. Le bloc formulaire de Framer ne peut pas changer son `action` : vous choisissez donc entre deux voies qui se comportent différemment une fois le vrai trafic arrivé.

## Ce qui change sur Framer

- **Pas d'action personnalisée.** Un formulaire Framer envoie vers les destinations de Framer : son propre stockage, l'e-mail, ou un **Webhook**. Un webhook est le seul moyen d'atteindre un backend externe sans code.
- **Les webhooks viennent des serveurs de Framer, pas du visiteur.** Ikiform voit les adresses IP de Framer et aucun en-tête `Origin`. Cela désactive trois protections : les domaines autorisés, la limite de débit par visiteur et la vérification humaine.
- **Nouvelles tentatives.** Framer traite tout ce qui n'est pas un `2xx` comme un échec et réessaie jusqu'à cinq fois. Il ne suit pas les redirections. Ikiform répond à un envoi JSON par `201` : une inscription stockée est donc un succès dès la première fois.
- **Les code components sont du vrai React.** Ils s'exécutent dans le navigateur du visiteur sur le site publié : un formulaire qu'ils contiennent envoie donc comme n'importe quelle page web.

Schéma : Deux façons dont une inscription Framer arrive à Ikiform

1. Avec la voie webhook, le visiteur envoie le formulaire Framer et les serveurs de Framer transmettent les champs à Ikiform en JSON.
2. Ikiform voit les serveurs de Framer comme expéditeur : il ne peut donc ni distinguer les visiteurs ni vérifier sur quel site se trouve le formulaire.
3. Avec le code component, le navigateur du visiteur envoie directement à Ikiform.
4. Ikiform voit l'IP de chaque visiteur et votre domaine : toutes les protections s'appliquent.

Les deux voies stockent la même inscription. Elles diffèrent par l'expéditeur que voit Ikiform.

## Choisir une voie

|   | Formulaire Framer avec un webhook | Code component |
| --- | --- | --- |
| Configuration | Deux champs dans la barre latérale droite | Un fichier de code, collé une fois |
| Style | Les styles et états du formulaire Framer | Des styles en ligne que vous modifiez, ou vos propres classes |
| Limite de débit par visiteur | Non. Tous les envois partagent les IP de Framer | Oui, 5 par minute chacun |
| Domaines autorisés | Laisser vide | Réglés sur vos domaines Framer et personnalisé |
| Honeypot | Non | Oui |
| Vérification humaine | Désactivée | Désactivée, car il envoie avec `fetch` |
| Suivi de la source | Aucune | `?ref=` dans l'URL, ou une valeur par défaut par instance |

La voie webhook convient à une page que vous partagez avec quelques centaines de personnes. Si vous êtes sur le point de publier la page quelque part de très visible, utilisez le code component : une vague d'inscriptions de bots via les serveurs de Framer ne peut être arrêtée que par le score de spam.

## Voie 1 : le formulaire Framer avec un webhook

1. Ajoutez à la page un formulaire avec un input **Email**. Dans les réglages de l'input, nommez-le `email`. Framer envoie le nom de chaque input comme clé JSON, et Ikiform utilise `email` pour l'adresse de réponse et la vérification des domaines jetables.
2. Sélectionnez le formulaire. À côté de **Send To**, choisissez **Add…** › **Webhook** et collez votre endpoint, `https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx`. Le secret de signature est facultatif ; Ikiform ne vérifie pas la signature de Framer.
3. Dans Ikiform, laissez **Allowed domains** vide et la vérification anti-spam désactivée. Avec l'un ou l'autre activé, chaque envoi Framer est signalé ou refusé, car il n'a pas d'origine et ne peut pas exécuter de vérification dans un navigateur.
4. Réglez la [limite de débit](https://ikiform.com/fr/docs/rate-limits.md) sur Relaxed, ou Custom le jour du lancement. Standard compte tous les envois de Framer comme un seul visiteur, et le formulaire entier est plafonné à 300 par minute.
5. Stylez l'état de succès du formulaire dans Framer. Il s'affiche quand Ikiform répond `201`.

> Un envoi refusé par Ikiform, comme un e-mail manquant avec une définition de champs en place, échoue de la même façon lors des cinq tentatives de Framer. Vérifiez la livraison dans Framer si les inscriptions cessent d'arriver.

## Voie 2 : un code component

Dans le panneau **Assets**, ouvrez **Code**, créez un fichier de code nommé `WaitlistSignup` et collez ceci. Faites-le glisser sur la page et définissez **Form ID** dans le panneau des propriétés.

`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" },
});
```

- Le panneau des propriétés expose le libellé du bouton, le texte de confirmation et une **Source** par défaut. Placez une instance dans le hero avec `hero` et une autre dans le pied de page avec `footer`, et vous verrez laquelle convertit.
- Un `?ref=` dans l'URL de la page l'emporte sur la valeur par défaut : les liens de campagne se taguent donc tout seuls.
- La clé d'idempotence est créée au premier envoi et réutilisée lors des nouvelles tentatives de la même visite : un double appui sur un téléphone ne stocke donc qu'une inscription.
- `window` n'est lu que dans le gestionnaire d'envoi. Framer rend d'abord les composants côté serveur, et toucher à `window` pendant le rendu casserait cela.

### Réglages Ikiform pour le composant

Ajoutez ceci dans l'onglet **Fields** du formulaire, pour que l'e-mail soit obligatoire et `ref` autorisé :

`Champs`:

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

- Dans **Allowed domains**, ajoutez `your-project.framer.app` et votre domaine personnalisé. N'ajoutez jamais `framer.app` seul ; tous les sites Framer correspondraient.
- Gardez la vérification anti-spam désactivée. Le composant envoie avec `fetch` sans récupérer de jeton : un formulaire avec la vérification activée répond donc `403 challenge_required`.
- Gardez les limites de débit Standard. Chaque visiteur est désormais compté séparément.

## Vérifiez que tout fonctionne

1. Publiez le site. Les webhooks et les code components n'envoient que depuis le site publié, pas depuis l'aperçu de l'éditeur.
2. Envoyez le formulaire sur votre domaine `framer.app`, puis à nouveau sur votre domaine personnalisé.
3. Ouvrez **Submissions** dans le tableau de bord. La nouvelle soumission y est, avec tous les champs envoyés.
4. Pour le composant, ouvrez la page avec `?ref=test` et vérifiez que l'inscription affiche `ref` à `test`.
5. Vérifiez la boîte de réception de chaque destinataire des notifications. Chacun reçoit un e-mail avec les champs, et **Reply** répond à l'expéditeur quand le formulaire a un champ `email`.
6. Rien n'est arrivé ? Passez le filtre de statut du tableau de bord sur **Spam**. Le spam reçoit la même réponse de succès qu'une vraie soumission.

## Pièges

| Problème | Que faire |
| --- | --- |
| Toutes les inscriptions par webhook sont en spam avec `origin_mismatch` | **Allowed domains** est défini, et les envois des serveurs de Framer n'ont pas d'origine. Videz-le pour ce formulaire, ou passez au code component. |
| Framer signale des livraisons de webhook échouées | Ikiform a refusé l'envoi : `403 challenge_required` avec la vérification anti-spam activée, ou `422` parce qu'un nom d'input n'est pas `email`. Corrigez la cause ; les nouvelles tentatives échouent de la même façon. |
| `429 rate_limited` | Un visiteur a envoyé plus que ce que permet la limite de débit du formulaire. Standard autorise 5 envois par minute et par visiteur. Attendez la durée de `Retry-After`, ou choisissez Relaxed ou Custom pour les formulaires qui envoient depuis une seule IP partagée. |
| Le composant affiche `Unknown field "ref"` | La définition des champs ne liste pas `ref`. Ajoutez-le comme dans la définition ci-dessus. |
| `403 origin_not_allowed`, ou tous les envois finissent en spam | Le domaine de la page n'est pas dans les **Allowed domains** du formulaire. Ajoutez-le, plus `localhost` pour les tests en local. Les sous-domaines d'un domaine listé sont autorisés. |

La configuration Framer complète, y compris la version formulaire de contact, est dans le [guide Framer de la documentation](https://ikiform.com/fr/docs/guides/framer.md). L'export de la liste et l'envoi de l'e-mail de lancement sont dans le [guide principal](https://ikiform.com/fr/guides/waitlist.md#launch-day).

## Questions

### Un formulaire Framer peut-il envoyer vers une URL externe ?

Pas comme action de formulaire. Les formulaires Framer envoient vers les destinations de Framer, dont un webhook vers n'importe quelle URL https. Utilisez-le pour Ikiform, ou affichez votre propre formulaire avec un code component.

### Pourquoi mes inscriptions Framer sont-elles marquées comme spam ?

Avec la voie webhook, Framer envoie depuis ses serveurs sans en-tête Origin : un formulaire avec Allowed domains défini marque donc chaque envoi comme origin_mismatch. Laissez Allowed domains vide pour les formulaires à webhook.

### Les inscriptions par webhook Framer comptent-elles dans les limites de formulaires de Framer ?

Framer indique que les soumissions de formulaire sont illimitées : les inscriptions par webhook ne se heurtent donc à aucun plafond Framer. La voie code component n'utilise pas du tout les formulaires de Framer.

### Ikiform est-il gratuit ?

Non, il n'y a pas d'offre gratuite. Les tests sont gratuits : vous pouvez envoyer 50 soumissions de test par mois depuis localhost avant de payer. Pour passer en production, Pro commence à 6 $/mois pour 500 soumissions, ou vous pouvez payer une seule fois 10 000 soumissions pour 49 $ ou 50 000 soumissions pour 149 $, avec des crédits qui n'expirent jamais.

## Autres guides

- [Liste d'attente pour un nouveau SaaS](https://ikiform.com/fr/guides/waitlist.md): Créez une liste d'attente avant lancement sur votre landing page : le formulaire, l'anti-spam, les doublons, les e-mails de confirmation et un export propre le jour J.
- [Liste d'attente sur Webflow](https://ikiform.com/fr/guides/waitlist/webflow.md): Faites pointer un Form Block Webflow vers Ikiform, taguez les inscriptions par source avec un champ masqué et gardez l'état de succès de Webflow avec un court script.
- [Liste d'attente sur Carrd](https://ikiform.com/fr/guides/waitlist/carrd.md): Collectez des inscriptions sur une page Carrd : un formulaire Send to URL sur Pro Plus avec suivi UTM, ou un formulaire HTML intégré avec honeypot sur Pro Standard.
- [Liste d'attente en HTML simple](https://ikiform.com/fr/guides/waitlist/html.md): Une page de liste d'attente complète en HTML simple, sans build, qui fonctionne sans JavaScript, remercie sur place quand il se charge, et se replie si besoin.
- [Liste de lancement Product Hunt](https://ikiform.com/fr/guides/waitlist/product-hunt.md): Collectez une liste des semaines avant Product Hunt, taguez chaque inscription par canal et envoyez l'e-mail du jour J en deux vagues calées sur l'heure du Pacifique.
- [Inscription bêta avec sélection](https://ikiform.com/fr/guides/waitlist/beta-signup.md): Un formulaire de candidature bêta avec des questions de sélection appliquées côté serveur, et un script qui note les candidats et choisit chaque cohorte.
