Warteliste für ein neues SaaS6 Min. Lesezeit
So fügst du einer Framer-Site eine Warteliste hinzu
Zwei Wege, auf Framer Wartelisten-Anmeldungen zu sammeln: Framers Formular mit einem Webhook an Ikiform oder eine Code-Komponente, die Limits pro Besucher und Spamprüfungen behält.
Framer-Formulare können nicht an eine externe URL senden, aber jede Einsendung an einen Webhook schicken, der No-Code-Weg ist also Framers Formular mit deinem Ikiform-Endpunkt als Webhook. Für einen öffentlichen Launch nimm stattdessen eine kleine Code-Komponente: Der Browser sendet direkt, Ikiforms Rate-Limit pro Besucher, erlaubte Domains und der Honeypot funktionieren also weiter.
Auf Framer entstehen viele Pre-Launch-Seiten, weil die Seite selbst einen Nachmittag dauert. Beim Wartelisten-Formular wird es weniger offensichtlich. Framers Formularblock kann seine action nicht ändern, du wählst also zwischen zwei Wegen, die sich unterschiedlich verhalten, sobald echter Traffic kommt.
Was auf Framer anders ist
- Keine eigene action. Ein Framer-Formular sendet an Framers Ziele: seinen eigenen Speicher, E-Mail oder einen Webhook. Ein Webhook ist der einzige Weg, ohne Code ein externes Backend zu erreichen.
- Webhooks kommen von Framers Servern, nicht vom Besucher. Ikiform sieht Framers IP-Adressen und keinen
Origin-Header. Das schaltet drei Schutzmechanismen ab: erlaubte Domains, das Rate-Limit pro Besucher und die Menschlichkeitsprüfung. - Wiederholungen. Framer behandelt alles außer einem
2xxals Fehler und versucht es bis zu fünfmal erneut. Es folgt keinen Weiterleitungen. Ikiform beantwortet einen JSON-Post mit201, eine gespeicherte Anmeldung ist also beim ersten Mal ein Erfolg. - Code-Komponenten sind echtes React. Sie laufen auf der veröffentlichten Site im Browser des Besuchers, ein Formular darin sendet also wie jede andere Webseite.
- Beim Webhook-Weg sendet der Besucher Framers Formular ab, und Framers Server leiten die Felder als JSON an Ikiform weiter.
- Ikiform sieht Framers Server als Absender, kann also Besucher nicht unterscheiden oder prüfen, auf welcher Site das Formular steht.
- Bei der Code-Komponente sendet der Browser des Besuchers direkt an Ikiform.
- Ikiform sieht die IP jedes Besuchers und deine Domain, also greift jeder Schutz.
Einen Weg wählen
| Item | Framer-Formular mit Webhook | Code-Komponente |
|---|---|---|
| Einrichtung | Zwei Felder in der rechten Seitenleiste | Eine Code-Datei, einmal eingefügt |
| Gestaltung | Framers Formular-Styles und -Zustände | Inline-Styles, die du bearbeitest, oder eigene Klassen |
| Rate-Limit pro Besucher | Nein. Alle Posts teilen sich Framers IPs | Ja, je 5 pro Minute |
| Erlaubte Domains | Leer lassen | Auf deine Framer- und eigenen Domains setzen |
| Honeypot | Nein | Ja |
| Menschlichkeitsprüfung | Aus | Aus, weil sie mit fetch sendet |
| Quellen-Tracking | Keine | ?ref= aus der URL oder ein Standardwert pro Instanz |
Der Webhook-Weg ist in Ordnung für eine Seite, die du mit ein paar Hundert Leuten teilst. Wenn du die Seite gleich irgendwo Großes postest, nimm die Code-Komponente: Einen Schub Bot-Anmeldungen über Framers Server kann nur die Spam-Bewertung stoppen.
Weg 1: Framers Formular mit einem Webhook
- Füge der Seite ein Formular mit einem Email-Eingabefeld hinzu. Setze in den Einstellungen des Felds seinen Namen auf
email. Framer sendet den Namen jedes Felds als JSON-Schlüssel, und Ikiform nutztemailfür Reply-to und die Prüfung auf Wegwerf-Domains. - Wähle das Formular aus. Wähle neben Send To Add… › Webhook und füge deinen Endpunkt
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxxein. Das Signing-Secret ist optional; Ikiform prüft Framers Signatur nicht. - Lass in Ikiform Allowed domains leer und die Spamprüfung aus. Ist eines davon aktiv, wird jeder Framer-Post markiert oder abgelehnt, weil er keinen Origin trägt und keine Browser-Prüfung ausführen kann.
- Setze das Rate-Limit auf Relaxed, oder am Launch-Tag auf Custom. Standard zählt alle Posts von Framer als einen Besucher, und das ganze Formular ist auf 300 pro Minute begrenzt.
- Gestalte den Erfolgszustand des Formulars in Framer. Er erscheint, wenn Ikiform mit
201antwortet.
Weg 2: eine Code-Komponente
Öffne im Panel Assets den Bereich Code, erstelle eine Code-Datei namens WaitlistSignup und füge das hier ein. Zieh sie auf die Seite und setze im Eigenschaften-Panel Form ID.
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" },
});- Das Eigenschaften-Panel bietet die Button-Beschriftung, den Text nach dem Eintragen und eine Standard-Source. Setz eine Instanz mit
heroin den Hero-Bereich und eine weitere mitfooterin den Footer, und du siehst, welche besser konvertiert. - Ein
?ref=in der Seiten-URL gewinnt gegen den Standardwert, Kampagnenlinks markieren sich also selbst. - Der Idempotenzschlüssel wird beim ersten Absenden erzeugt und bei Wiederholungen aus demselben Besuch wiederverwendet, ein Doppeltipp am Handy speichert also eine Anmeldung.
windowwird nur im Submit-Handler gelesen. Framer rendert Komponenten zuerst auf dem Server, und ein Zugriff aufwindowbeim Rendern würde das kaputt machen.
Ikiform-Einstellungen für die Komponente
Füge diese im Tab Fields des Formulars hinzu, damit die E-Mail Pflicht ist und ref erlaubt ist:
{
"version": 1,
"title": "Waitlist",
"fields": [
{ "id": "email", "type": "email", "label": "Email", "required": true },
{ "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
]
}- Füge unter Allowed domains
your-project.framer.appund deine eigene Domain hinzu. Füge nieframer.appallein hinzu; jede Framer-Site würde passen. - Lass die Spamprüfung aus. Die Komponente sendet mit
fetchund holt kein Token, ein Formular mit aktiver Prüfung antwortet also mit403 challenge_required. - Behalte die Standard-Rate-Limits. Jeder Besucher wird jetzt einzeln gezählt.
Prüfen, ob es funktioniert
- Veröffentliche die Site. Webhooks und Code-Komponenten senden nur von der veröffentlichten Site, nicht aus der Vorschau im Editor.
- Sende das Formular auf deiner
framer.app-Domain ab und noch einmal auf deiner eigenen Domain. - Öffne Submissions im Dashboard. Die neue Einsendung ist da, mit jedem Feld, das du gesendet hast.
- Öffne für die Komponente die Seite mit
?ref=testund prüfe, dass die Anmeldungrefalstestzeigt. - Prüfe das Postfach jedes Benachrichtigungsempfängers. Jeder bekommt eine E-Mail mit den Feldern, und Reply geht an den Absender, wenn das Formular ein
email-Feld hat. - Nichts angekommen? Stell den Statusfilter im Dashboard auf Spam. Spam bekommt dieselbe Erfolgsantwort wie eine echte Einsendung.
Stolperfallen
| Problem | Was zu tun ist |
|---|---|
Jede Webhook-Anmeldung liegt mit origin_mismatch im Spam | Allowed domains ist gesetzt, und Framers Server-Posts haben keinen Origin. Leere es für dieses Formular oder wechsle zur Code-Komponente. |
| Framer meldet fehlgeschlagene Webhook-Zustellungen | Ikiform hat den Post abgelehnt: 403 challenge_required bei aktiver Spamprüfung oder 422, weil ein Feldname nicht email ist. Beheb die Ursache; Wiederholungen scheitern genauso. |
429 rate_limited | Ein Besucher hat mehr gesendet, als das Rate-Limit des Formulars erlaubt. Standard erlaubt 5 pro Minute und Besucher. Warte Retry-After ab oder wähle Relaxed oder Custom für Formulare, die von einer gemeinsamen IP aus senden. |
Die Komponente zeigt Unknown field "ref" | Die Felddefinition listet ref nicht auf. Füge es wie in der Definition oben hinzu. |
403 origin_not_allowed, oder jeder Post landet im Spam | Die Domain der Seite steht nicht unter Allowed domains des Formulars. Füge sie hinzu, plus localhost für lokale Tests. Subdomains einer eingetragenen Domain sind erlaubt. |
Die vollständige Framer-Einrichtung, einschließlich der Kontaktformular-Version, steht in der Framer-Anleitung in der Doku. Export der Liste und Versand der Launch-E-Mail stehen in der Hauptanleitung.
Fragen
Nicht als Form-Action. Framer-Formulare senden an Framers eigene Ziele, und eines davon ist ein Webhook an eine beliebige https-URL. Nutze den für Ikiform oder rendere mit einer Code-Komponente dein eigenes Formular.
Beim Webhook-Weg sendet Framer von seinen Servern ohne Origin-Header, ein Formular mit gesetzten Allowed domains markiert also jeden Post als origin_mismatch. Lass Allowed domains bei Webhook-Formularen leer.
Laut Framer sind Formulareinsendungen unbegrenzt, Webhook-Anmeldungen stoßen also an keine Framer-Grenze. Der Weg über die Code-Komponente nutzt Framers Formulare gar nicht.
Nein, es gibt keinen kostenlosen Tarif. Testen ist kostenlos: Du kannst 50 Testeinsendungen pro Monat von localhost senden, bevor du zahlst. Für den Livebetrieb startet Pro bei $6/Monat für 500 Einsendungen, oder du zahlst einmalig 10.000 Einsendungen für $49 oder 50.000 Einsendungen für $149, mit Guthaben, das nie verfällt.
Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.