Platform-agnostic waitlist registration form component with email/phone capture, bot protection, geo-detection, and SMS consent handling. ## Key Components ### `WaitlistFormProps` | Prop | Type | Default | Description | |---|---|---|---| | `onRegister` | `(email, phone?, signals?) => Promise` | — | **Required.** Registration handler; must throw on failure | | `isSubmitting` | `boolean` | `false` | Controls loading state | | `isSuccess` | `boolean` | `false` | Toggles success label on submit button | | `defaultEmail` | `string` | `''` | Pre-filled email | | `defaultPhone` | `string` | `''` | Pre-filled phone | | `geoApiUrl` | `string \| null` | `'/api/geo'` | Geo-detection endpoint; set to `null` to disable | | `submitLabel` | `string` | `'Get Beta Access'` | Submit button label | | `successLabel` | `string` | `"You're in!"` | Post-success button label | | `termsOfServiceUrl` | `string` | — | ToS link in consent text | | `privacyPolicyUrl` | `string` | — | Privacy Policy link in consent text | ### `WaitlistForm` The exported form component. Internally manages: - **Email validation** — warns on generic domains via `hasGenericEmailDomain` - **Phone handling** — E.164 formatting via `formatPhoneE164`; country auto-detected from geo API - **Bot protection** — invisible `HoneypotField` + timing signals via `useHumanitySignals` - **SMS consent gate** — blocks submission if phone is provided without consent - **Hydration safety** — renders an animated skeleton until client-side hydration completes ## Usage Example ```typescript import { WaitlistForm } from './waitlist-form' async function register(email: string, phone?: string, signals?: HumanitySignals) { const res = await fetch('/api/waitlist', { method: 'POST', body: JSON.stringify({ email, phone, ...signals }), }) if (!res.ok) throw new Error('Registration failed') } export default function Page() { const [isSubmitting, setIsSubmitting] = useState(false) const [isSuccess, setIsSuccess] = useState(false) return ( { setIsSubmitting(true) await register(email, phone, signals) setIsSuccess(true) setIsSubmitting(false) }} isSubmitting={isSubmitting} isSuccess={isSuccess} geoApiUrl="/api/geo" termsOfServiceUrl="https://flamingo.run/terms" privacyPolicyUrl="https://flamingo.run/privacy" /> ) } ``` **Source:** [`waitlist-form.tsx`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/waitlist-form.tsx)