What it is
The Waitlist module gives you a public, conversion-focused landing page at /waitlist where visitors enter their email (and optionally name + referral source) to join your early-access list. Each submission is persisted to the database and triggers a confirmation email.
Use it when you want to:
- Launch a "coming soon" page while building.
- Gate signups behind an approval flow (beta / invite-only).
- Collect demand signal before going public.
How to enable / disable
Enabled by default. Toggle with a single env var — no code changes.
# Disable the public /waitlist page and the admin/waitlist sidebar entry
ENABLE_WAITLIST="false"
When disabled:
/waitlistreturns 404 (viasrc/proxy.ts).- The
Waitlistlink is removed from the marketing navbar. - The
/admin/waitlistlink is hidden from the admin sidebar. - The waitlist URL is removed from
sitemap.xml.
Dashboard/admin API routes continue to exist; the flag only controls visibility of UI surfaces.
Where customers find it
- Marketing navbar — a
Waitlistlink is shown whenENABLE_WAITLISTis on. - Direct URL —
/<locale>/waitlist(indexed in the sitemap).
If you want to route visitors from a "Coming soon" hero instead of the regular pricing page, remove /pricing from the navbar and point the Hero CTA to /waitlist by editing src/components/marketing/Hero.tsx.
Files
| File | Purpose |
|---|---|
src/app/[locale]/(marketing)/waitlist/page.tsx | Public landing page (server component, i18n-ready). |
src/components/marketing/WaitlistForm.tsx | Client form with inline success / error state. |
src/lib/actions/waitlist.ts | Server action joinWaitlist — validation, dedupe, email. |
src/lib/db/schema.ts (waitlist_entry table) | Persistence (email unique, optional name + referral source). |
src/app/[locale]/admin/waitlist/ | Admin list view (entries, export, etc.). |
Flow
- Visitor submits the form on
/waitlist. joinWaitlistserver action validates input with Zod and checks a shared rate limit (waitlist:global, 60 req/min).- If the email is new, a row is inserted into
waitlist_entry; duplicates return a friendly "already on the list" message. - A confirmation email is sent via the Email module (
sendEmail). Failures are logged but do not block the response. - Admins can review submissions at
/admin/waitlist.
i18n
Copy lives under the Waitlist namespace in messages/<locale>.json. Required keys: pageTitle, pageSubtitle, noSpam, plus form/error strings used by WaitlistForm.
Removing the module
If you don't need waitlist at all (not just temporarily disabled), remove it entirely:
- Delete
src/app/[locale]/(marketing)/waitlist/,src/app/[locale]/admin/waitlist/,src/lib/actions/waitlist.ts,src/components/marketing/WaitlistForm.tsx. - Drop the
waitlist_entrytable fromsrc/lib/db/schema.tsand regenerate migrations (pnpm run db:generate). - Remove the
waitlistentries fromsrc/config/navigation.tsand theblog/waitlistbranches insrc/proxy.ts,src/app/sitemap.ts, andsrc/components/marketing/Navbar.tsx. - Remove the
waitlistflag fromenv.featuresinsrc/lib/config.tsand dropENABLE_WAITLISTfrom.env.example. - Remove the
Waitlistnamespace from eachmessages/<locale>.json.