Enable prelaunch mode to collect waitlist signups before your product launches.
Toggle with NEXT_PUBLIC_PRELAUNCH=true in your .env.local, or configure it during npm run init (Step 2c: Features).
In application code, always read the flag via appConfig.features.prelaunch rather than process.env.NEXT_PUBLIC_PRELAUNCH directly.
What Changes in Prelaunch Mode
| Component | Normal Mode | Prelaunch Mode |
|---|---|---|
| Hero Section | CTA buttons (Get Started, Learn More) | Waitlist signup form |
| Social Proof | Visible (user count, ratings) | Hidden |
| Login Button | Visible in navbar | Hidden (desktop & mobile) |
| Sign-In (server-side) | Open to anyone | Allowlist-gated (see below). Login page remains reachable for admins. |
| Pricing Buttons | Buy/Subscribe buttons | "Coming soon" (disabled) |
| Testimonials | Visible | Hidden |
Sign-In Allowlist (server-side gate)
Hiding the login button is purely cosmetic — nothing prevents a visitor from typing /login directly or completing an in-flight OAuth callback. Prelaunch mode therefore also gates authentication server-side via an email allowlist:
# Server-only (no NEXT_PUBLIC_ prefix). Comma-separated, case-insensitive.
# Empty = sign-in is fully locked (you'll be unable to log in yourself).
[email protected],[email protected]
The gate fires at three points, all routed through the isPrelaunchEmailAllowed() helper exported from config/app.ts:
| Enforcement point | Behavior on non-allowlisted email |
|---|---|
POST /api/auth/magic-link |
Returns 403 with code PRELAUNCH_RESTRICTED before calling Supabase generateLink, so no auth.users row is created. |
GET /[locale]/callback (magic-link path) |
Belt-and-suspenders: if the allowlist shrinks while a valid link is in flight, the user is signed out and redirected to ?error=prelaunch. |
GET /[locale]/callback (OAuth path) |
OAuth always creates the auth user during exchangeCodeForSession. The handler signs out and calls supabaseAdmin.auth.admin.deleteUser(...) to undo the cascade-created profile/account/membership. Without this, every OAuth attempt by a stranger would leave an orphan auth row — exactly the symptom that motivated the gate. |
Operator notes
- Add yourself to
PRELAUNCH_ALLOWED_EMAILSbefore flippingNEXT_PUBLIC_PRELAUNCH=true, or you'll lock yourself out. - The error message is generic ("Authentication is currently restricted to invited users") to avoid revealing whether an email exists in your DB.
- The login page shows a localized amber banner explaining the restriction (translation key
auth.login.prelaunchRestricted). - Orphan
auth.usersrows created before the gate was wired need manual cleanup in the Supabase dashboard.
Waitlist Features
- Email validation with error states
- Loading spinner during submission
- Success confirmation with check icon
- Optional spots remaining counter
- Optional total waiters count
- Newsletter integration via pluggable email provider (Brevo / Mailjet)
- Three variants: default, centered, glass card