Daftar tunggu untuk SaaS baru

Daftar tunggu untuk SaaS baru6 menit baca

Cara menambahkan daftar tunggu ke situs Framer

Dua cara mengumpulkan pendaftaran daftar tunggu di Framer: formulir Framer dengan webhook ke Ikiform, atau code component yang mempertahankan batas per pengunjung dan pemeriksaan spam.

Preet SutharPendiri, Ikiform

Formulir Framer tidak bisa mengirim ke URL luar, tetapi bisa mengirim setiap kiriman ke webhook, jadi jalur tanpa kode adalah formulir Framer dengan endpoint Ikiform Anda sebagai webhook. Untuk peluncuran publik, gunakan code component kecil: browser mengirim langsung, jadi rate limit per pengunjung, domain yang diizinkan, dan honeypot dari Ikiform tetap berfungsi.

Banyak halaman pra-peluncuran dibangun di Framer, karena halamannya sendiri bisa selesai dalam satu sore. Formulir daftar tunggu adalah bagian yang kurang jelas. Blok formulir Framer tidak bisa mengubah action-nya, jadi Anda memilih antara dua jalur yang berperilaku berbeda begitu trafik sungguhan datang.

Apa bedanya di Framer

  • Tidak ada action kustom. Formulir Framer mengirim ke tujuan milik Framer: penyimpanannya sendiri, email, atau Webhook. Webhook adalah satu-satunya cara menjangkau backend luar tanpa kode.
  • Webhook datang dari server Framer, bukan dari pengunjung. Ikiform melihat alamat IP Framer dan tidak ada header Origin. Itu mematikan tiga perlindungan: domain yang diizinkan, rate limit per pengunjung, dan pemeriksaan manusia.
  • Percobaan ulang. Framer menganggap apa pun selain 2xx sebagai kegagalan dan mencoba ulang hingga lima kali. Framer tidak mengikuti redirect. Ikiform menjawab kiriman JSON dengan 201, jadi pendaftaran yang tersimpan langsung dianggap sukses sejak percobaan pertama.
  • Code component adalah React sungguhan. Komponen ini berjalan di browser pengunjung di situs yang sudah dipublikasikan, jadi formulir di dalamnya mengirim seperti halaman web lain.
Dua cara pendaftaran daftar tunggu Framer sampai ke IkiformFormulir Framer dengan webhookCode componentFormulir Framerdi halaman AndaServer FramerJSON, dicoba ulanghingga 5 kaliIkiformmelihat IP Framer,tanpa origincode componentFormulir React dihalaman Andabrowserpengunjungfetch denganidempotency keyIkiformmelihat pengunjungdan domain AndakirimwebhookkirimkirimanDua cara pendaftaran daftar tunggu Framer sampai ke IkiformFormulir Framer dengan webhookCode componentFormulir Framerdi halaman AndaServer FramerJSON, dicoba ulang hingga 5kaliIkiformmelihat IP Framer, tanpa origincode componentFormulir React di halaman Andabrowser pengunjungfetch dengan idempotency keyIkiformmelihat pengunjung dan domainAndakirimwebhookkirimkiriman
  1. Dengan jalur webhook, pengunjung mengirim formulir Framer dan server Framer meneruskan field-nya ke Ikiform sebagai JSON.
  2. Ikiform melihat server Framer sebagai pengirim, jadi tidak bisa membedakan pengunjung atau memeriksa di situs mana formulir itu berada.
  3. Dengan code component, browser pengunjung mengirim langsung ke Ikiform.
  4. Ikiform melihat IP setiap pengunjung dan domain Anda, jadi setiap perlindungan berlaku.
Kedua jalur menyimpan pendaftaran yang sama. Bedanya ada pada siapa yang dianggap Ikiform sebagai pengirimnya.

Pilih jalur

ItemFormulir Framer dengan webhookCode component
SetupDua field di sidebar kananSatu file kode, ditempel sekali
GayaGaya dan state formulir FramerInline style yang Anda edit, atau class Anda sendiri
Rate limit per pengunjungTidak. Semua kiriman berbagi IP FramerYa, masing-masing 5 per menit
Domain yang diizinkanBiarkan kosongIsi dengan domain Framer dan domain kustom Anda
HoneypotTidakYa
Pemeriksaan manusiaMatiMati, karena mengirim dengan fetch
Pelacakan sumberTidak ada?ref= dari URL, atau default per instance

Jalur webhook sudah cukup untuk halaman yang Anda bagikan ke beberapa ratus orang. Jika Anda akan membagikan halaman di tempat besar, gunakan code component: lonjakan pendaftaran bot lewat server Framer hanya bisa dihentikan oleh penilaian spam.

Jalur 1: formulir Framer dengan webhook

  1. Tambahkan formulir ke halaman dengan satu input Email. Di pengaturan input, atur namanya ke email. Framer mengirim nama setiap input sebagai key JSON, dan Ikiform memakai email untuk reply-to dan pemeriksaan domain sekali pakai.
  2. Pilih formulirnya. Di samping Send To, pilih Add… › Webhook dan tempel endpoint Anda, https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx. Signing secret bersifat opsional; Ikiform tidak memeriksa tanda tangan Framer.
  3. Di Ikiform, biarkan Allowed domains kosong dan spam check mati. Jika salah satunya aktif, setiap kiriman Framer akan ditandai atau ditolak, karena tidak membawa origin dan tidak bisa menjalankan pemeriksaan browser.
  4. Atur rate limit ke Relaxed, atau Custom di hari peluncuran. Standard menghitung semua kiriman Framer sebagai satu pengunjung, dan seluruh formulir dibatasi 300 per menit.
  5. Atur gaya state sukses formulir di Framer. State ini muncul saat Ikiform menjawab 201.
Kiriman yang ditolak Ikiform, seperti email yang tidak ada saat definisi field terpasang, gagal dengan cara yang sama di kelima percobaan ulang Framer. Periksa pengirimannya di Framer jika pendaftaran berhenti masuk.

Jalur 2: code component

Di panel Assets, buka Code, buat file kode bernama WaitlistSignup, lalu tempel ini. Seret ke halaman dan atur Form ID di panel properti.

WaitlistSignup.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" },
});
  • Panel properti menampilkan label tombol, teks setelah bergabung, dan Source default. Taruh satu instance di hero dengan hero dan satu lagi di footer dengan footer, dan Anda akan melihat mana yang lebih banyak menghasilkan pendaftaran.
  • ?ref= di URL halaman lebih diutamakan daripada default, jadi link kampanye menandai dirinya sendiri.
  • Idempotency key dibuat saat pengiriman pertama dan dipakai ulang pada percobaan ulang dari kunjungan yang sama, jadi ketukan ganda di ponsel hanya menyimpan satu pendaftaran.
  • window hanya dibaca di dalam submit handler. Framer merender komponen di server lebih dulu, dan menyentuh window saat render akan merusaknya.

Pengaturan Ikiform untuk komponen

Tambahkan ini di tab Fields formulir, agar email wajib diisi dan ref diizinkan:

Field
{
  "version": 1,
  "title": "Waitlist",
  "fields": [
    { "id": "email", "type": "email", "label": "Email", "required": true },
    { "id": "ref", "type": "text", "label": "Source", "validation": { "max_length": 40 } }
  ]
}
  • Di Allowed domains, tambahkan your-project.framer.app dan domain kustom Anda. Jangan pernah menambahkan framer.app saja; setiap situs Framer akan cocok.
  • Biarkan spam check mati. Komponen mengirim dengan fetch dan tidak mengambil token, jadi formulir dengan pemeriksaan aktif akan menjawab 403 challenge_required.
  • Pertahankan rate limit Standard. Setiap pengunjung kini dihitung masing-masing.

Pastikan berfungsi

  1. Publikasikan situsnya. Webhook dan code component hanya mengirim dari situs yang sudah dipublikasikan, bukan dari preview editor.
  2. Kirim formulir di domain framer.app Anda, lalu lagi di domain kustom Anda.
  3. Buka Submissions di dashboard. Kiriman baru ada di sana dengan setiap field yang Anda kirim.
  4. Untuk komponen, buka halaman dengan ?ref=test dan pastikan pendaftaran menampilkan ref sebagai test.
  5. Periksa inbox setiap penerima notifikasi. Masing-masing mendapat email berisi field-field tersebut, dan Reply ditujukan ke pengirim jika formulir punya field email.
  6. Tidak ada yang masuk? Ubah filter status di dashboard ke Spam. Spam mendapat jawaban sukses yang sama dengan kiriman asli.

Jebakan

MasalahYang harus dilakukan
Setiap pendaftaran webhook masuk spam dengan origin_mismatchAllowed domains sudah diatur, sedangkan kiriman dari server Framer tidak punya origin. Kosongkan untuk formulir ini, atau beralih ke code component.
Framer melaporkan pengiriman webhook yang gagalIkiform menolak kirimannya: 403 challenge_required karena spam check aktif, atau 422 karena nama input bukan email. Perbaiki penyebabnya; percobaan ulang akan gagal dengan cara yang sama.
429 rate_limitedSatu pengunjung mengirim melebihi rate limit formulir. Standard mengizinkan 5 per menit per pengunjung. Tunggu sesuai Retry-After, atau pilih Relaxed atau Custom untuk formulir yang mengirim dari satu IP bersama.
Komponen menampilkan Unknown field "ref"Definisi field tidak mencantumkan ref. Tambahkan seperti pada definisi di atas.
403 origin_not_allowed, atau setiap kiriman masuk ke spamDomain halaman tidak ada di Allowed domains formulir. Tambahkan, ditambah localhost untuk tes lokal. Subdomain dari domain yang tercantum diizinkan.

Setup Framer lengkap, termasuk versi formulir kontak, ada di panduan dokumentasi Framer. Ekspor daftar dan pengiriman email peluncuran ada di panduan utama.

Pertanyaan

Mentok di salah satu langkah, atau ingin panduan untuk situasi Anda? Email hello@ikiform.com.