Waitlist System
Build anticipation, collect early interest, and manage user access with a powerful waitlist system that grows your audience.
RankThis includes a complete waitlist system for collecting emails, managing early access, and building excitement before your official launch.
✅ Key Features
- • Email collection with validation
- • Duplicate prevention
- • Admin management dashboard
- • Bulk email activation/deactivation
- • Export functionality
- • Customizable signup forms
🎯 Use Cases
- • Pre-launch email collection
- • Early access management
- • Feature beta testing
- • Marketing campaign building
- • User interest validation
- • Product-market fit testing
Implementation
The waitlist uses a simple but effective database structure:
1model WaitlistEntry {2 id String @id @default(cuid())3 email String @unique4 isActive Boolean @default(true)5 createdAt DateTime @default(now())6 updatedAt DateTime @updatedAt78 @@map("waitlist_entries")9}
Schema Fields
- •
email- Unique constraint prevents duplicates - •
isActive- Toggle for email status - •
createdAt- Track signup timing
Benefits
- • Fast email lookups
- • Prevents spam/duplicates
- • Easy bulk operations
Server actions handle waitlist operations with proper validation:
1"use server";23import { db } from "~/server/db";4import { z } from "zod";56const addToWaitlistSchema = z.object({7 email: z.string().email("Please enter a valid email address"),8});910export async function addToWaitlist(formData: FormData) {11 try {12 const email = formData.get("email") as string;1314 // Validate email format15 const validatedData = addToWaitlistSchema.parse({ email });1617 // Check if email already exists18 const existing = await db.waitlistEntry.findUnique({19 where: { email: validatedData.email },20 });2122 if (existing) {23 return {24 success: false,25 error: "Email is already on the waitlist!"26 };27 }2829 // Add to waitlist30 await db.waitlistEntry.create({31 data: {32 email: validatedData.email,33 isActive: true,34 },35 });3637 return {38 success: true,39 message: "Successfully added to waitlist!"40 };4142 } catch (error) {43 if (error instanceof z.ZodError) {44 return {45 success: false,46 error: error.errors[0]?.message ?? "Invalid email"47 };48 }4950 return {51 success: false,52 error: "Failed to join waitlist. Please try again."53 };54 }55}5657export async function getWaitlistStats() {58 const [total, active] = await Promise.all([59 db.waitlistEntry.count(),60 db.waitlistEntry.count({ where: { isActive: true } }),61 ]);6263 return { total, active, inactive: total - active };64}
Waitlist Components
Simple, conversion-optimized signup form:
1"use client";23import { useState } from "react";4import { Button } from "~/components/ui/button";5import { addToWaitlist } from "~/server/actions/waitlist";67export function WaitlistSignup() {8 const [message, setMessage] = useState<string>("");9 const [isLoading, setIsLoading] = useState(false);1011 async function handleSubmit(formData: FormData) {12 setIsLoading(true);13 setMessage("");1415 const result = await addToWaitlist(formData);1617 if (result.success) {18 setMessage(result.message!);19 // Reset form20 const form = document.getElementById("waitlist-form") as HTMLFormElement;21 form?.reset();22 } else {23 setMessage(result.error!);24 }2526 setIsLoading(false);27 }2829 return (30 <div className="mx-auto max-w-md space-y-4">31 <form id="waitlist-form" action={handleSubmit} className="space-y-3">32 <div className="flex gap-2">33 <input34 type="email"35 name="email"36 placeholder="Enter your email"37 required38 className="flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm"39 />40 <Button type="submit" disabled={isLoading}>41 {isLoading ? "Joining..." : "Join Waitlist"}42 </Button>43 </div>44 </form>4546 {message && (47 <p className={`text-sm ${48 message.includes("Successfully")49 ? "text-green-600 dark:text-green-400"50 : "text-red-600 dark:text-red-400"51 }`}>52 {message}53 </p>54 )}55 </div>56 );57}
Full-featured section with social proof and benefits:
1"use client";23import { useState, useEffect } from "react";4import { WaitlistSignup } from "./WaitlistSignup";5import { getWaitlistStats } from "~/server/actions/waitlist";67export function WaitlistSection() {8 const [stats, setStats] = useState({ total: 0, active: 0 });910 useEffect(() => {11 getWaitlistStats().then(setStats);12 }, []);1314 return (15 <section className="py-16 px-4">16 <div className="mx-auto max-w-4xl text-center">17 <h2 className="mb-4 text-3xl font-bold">18 Join the Waitlist19 </h2>2021 <p className="mb-8 text-lg text-muted-foreground">22 Be the first to know when we launch. Get early access and exclusive updates.23 </p>2425 {/* Social Proof */}26 {stats.total > 0 && (27 <div className="mb-8">28 <p className="text-sm text-muted-foreground">29 Join <strong>{stats.total}</strong> others already on the waitlist30 </p>31 </div>32 )}3334 {/* Signup Form */}35 <WaitlistSignup />3637 {/* Benefits */}38 <div className="mt-12 grid gap-6 md:grid-cols-3">39 <div className="text-center">40 <div className="mb-2 text-2xl">🚀</div>41 <h3 className="font-semibold">Early Access</h3>42 <p className="text-sm text-muted-foreground">43 Get access before the public launch44 </p>45 </div>4647 <div className="text-center">48 <div className="mb-2 text-2xl">💰</div>49 <h3 className="font-semibold">Special Pricing</h3>50 <p className="text-sm text-muted-foreground">51 Exclusive discounts for early supporters52 </p>53 </div>5455 <div className="text-center">56 <div className="mb-2 text-2xl">📧</div>57 <h3 className="font-semibold">Updates</h3>58 <p className="text-sm text-muted-foreground">59 Behind-the-scenes updates and progress60 </p>61 </div>62 </div>63 </div>64 </section>65 );66}
API Management
RESTful API endpoints for waitlist management:
1// GET /api/waitlist/entries - Get all waitlist entries2export async function GET() {3 try {4 const entries = await db.waitlistEntry.findMany({5 orderBy: { createdAt: "desc" },6 });78 return Response.json({ entries });9 } catch (error) {10 return Response.json(11 { error: "Failed to fetch entries" },12 { status: 500 }13 );14 }15}1617// POST /api/waitlist/activate - Bulk activate emails18export async function POST(request: Request) {19 const { emails } = await request.json();2021 await db.waitlistEntry.updateMany({22 where: { email: { in: emails } },23 data: { isActive: true },24 });2526 return Response.json({ success: true });27}2829// POST /api/waitlist/deactivate - Bulk deactivate emails30export async function POST(request: Request) {31 const { emails } = await request.json();3233 await db.waitlistEntry.updateMany({34 where: { email: { in: emails } },35 data: { isActive: false },36 });3738 return Response.json({ success: true });39}
Integration Examples
Add waitlist to your landing page:
1// src/app/page.tsx2import { WaitlistSection } from "~/components/WaitlistSection";34export default function HomePage() {5 return (6 <main>7 {/* Hero Section */}8 <section className="py-20">9 <h1>Your Amazing SaaS Product</h1>10 <p>Revolutionary features coming soon...</p>11 </section>1213 {/* Features */}14 <section className="py-16">15 {/* Feature content */}16 </section>1718 {/* Waitlist CTA */}19 <WaitlistSection />2021 {/* Footer */}22 </main>23 );24}
Trigger waitlist signup with exit-intent or timing:
1"use client";23import { useState, useEffect } from "react";4import { Dialog, DialogContent, DialogHeader, DialogTitle } from "~/components/ui/dialog";5import { WaitlistSignup } from "~/components/WaitlistSignup";67export function WaitlistModal() {8 const [isOpen, setIsOpen] = useState(false);910 useEffect(() => {11 // Show after 30 seconds or on exit intent12 const timer = setTimeout(() => {13 setIsOpen(true);14 }, 30000);1516 const handleMouseLeave = (e: MouseEvent) => {17 if (e.clientY <= 0) {18 setIsOpen(true);19 }20 };2122 document.addEventListener("mouseleave", handleMouseLeave);2324 return () => {25 clearTimeout(timer);26 document.removeEventListener("mouseleave", handleMouseLeave);27 };28 }, []);2930 return (31 <Dialog open={isOpen} onOpenChange={setIsOpen}>32 <DialogContent>33 <DialogHeader>34 <DialogTitle>Don't Miss Out! 🚀</DialogTitle>35 </DialogHeader>3637 <div className="space-y-4">38 <p>39 Join our waitlist to get early access and exclusive updates40 about our launch.41 </p>4243 <WaitlistSignup />4445 <p className="text-xs text-muted-foreground">46 We respect your privacy. Unsubscribe at any time.47 </p>48 </div>49 </DialogContent>50 </Dialog>51 );52}
Email Campaigns
Send beautiful emails to your waitlist:
1// Welcome email for new waitlist members2export function generateWaitlistWelcomeEmail(email: string) {3 return `4 <!DOCTYPE html>5 <html>6 <head>7 <title>Welcome to the Waitlist!</title>8 </head>9 <body style="font-family: system-ui, sans-serif; max-width: 600px; margin: 0 auto;">10 <div style="padding: 40px; text-align: center;">11 <h1 style="color: #1a202c; margin-bottom: 20px;">12 🎉 You're on the waitlist!13 </h1>1415 <p style="color: #4a5568; line-height: 1.6; margin-bottom: 30px;">16 Thanks for joining our waitlist! You'll be among the first to know17 when we launch and get exclusive early access.18 </p>1920 <div style="background: #f7fafc; padding: 20px; border-radius: 8px; margin: 20px 0;">21 <h3 style="margin: 0; color: #2d3748;">What's Next?</h3>22 <ul style="text-align: left; color: #4a5568;">23 <li>We'll send you updates as we build</li>24 <li>Early access before public launch</li>25 <li>Special pricing for waitlist members</li>26 </ul>27 </div>2829 <p style="color: #718096; font-size: 14px;">30 Follow our progress on social media for behind-the-scenes updates!31 </p>32 </div>33 </body>34 </html>35 `;36}3738// Launch announcement email39export function generateLaunchAnnouncementEmail() {40 return `41 <!DOCTYPE html>42 <html>43 <body style="font-family: system-ui, sans-serif; max-width: 600px; margin: 0 auto;">44 <div style="padding: 40px; text-align: center;">45 <h1 style="color: #1a202c;">🚀 We're Live!</h1>4647 <p style="color: #4a5568; line-height: 1.6;">48 The wait is over! Our platform is now live and ready for you to explore.49 </p>5051 <a href="${process.env.COMPANY_URL}/auth/signin"52 style="display: inline-block; background: #4299e1; color: white;53 padding: 12px 24px; text-decoration: none; border-radius: 6px;54 font-weight: bold; margin: 20px 0;">55 Get Started Now56 </a>5758 <p style="color: #4a5568; font-size: 14px;">59 As a waitlist member, you get 50% off your first month!<br>60 Use code: <strong>EARLYBIRD50</strong>61 </p>62 </div>63 </body>64 </html>65 `;66}
Growth Strategies
- • Add social proof (current member count)
- • Offer exclusive benefits
- • Create referral incentives
- • Share behind-the-scenes updates
- • Use exit-intent popups
Email Engagement
- • Send welcome emails immediately
- • Share development progress
- • Ask for feedback and input
- • Provide early access perks
- • Keep emails concise and valuable
Key Metrics to Track
- • Signup conversion rates by traffic source
- • Email engagement rates (opens, clicks)
- • Waitlist-to-customer conversion
- • Time between signup and launch
PostHog Integration Example
1// Track waitlist signups2posthog.capture('waitlist_signup', {3 email_domain: email.split('@')[1],4 source: document.referrer,5 page_url: window.location.href,6});
Waitlist System Ready!
Your waitlist system is configured and ready to collect early interest. Next, learn about the admin panel for managing your growing user base.