Waitlist System

Build anticipation, collect early interest, and manage user access with a powerful waitlist system that grows your audience.

Waitlist System Overview

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

Database Schema

The waitlist uses a simple but effective database structure:

prisma/schema.prisma
1model WaitlistEntry {
2 id String @id @default(cuid())
3 email String @unique
4 isActive Boolean @default(true)
5 createdAt DateTime @default(now())
6 updatedAt DateTime @updatedAt
7
8 @@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
Waitlist Actions

Server actions handle waitlist operations with proper validation:

src/server/actions/waitlist.ts
1"use server";
2
3import { db } from "~/server/db";
4import { z } from "zod";
5
6const addToWaitlistSchema = z.object({
7 email: z.string().email("Please enter a valid email address"),
8});
9
10export async function addToWaitlist(formData: FormData) {
11 try {
12 const email = formData.get("email") as string;
13
14 // Validate email format
15 const validatedData = addToWaitlistSchema.parse({ email });
16
17 // Check if email already exists
18 const existing = await db.waitlistEntry.findUnique({
19 where: { email: validatedData.email },
20 });
21
22 if (existing) {
23 return {
24 success: false,
25 error: "Email is already on the waitlist!"
26 };
27 }
28
29 // Add to waitlist
30 await db.waitlistEntry.create({
31 data: {
32 email: validatedData.email,
33 isActive: true,
34 },
35 });
36
37 return {
38 success: true,
39 message: "Successfully added to waitlist!"
40 };
41
42 } catch (error) {
43 if (error instanceof z.ZodError) {
44 return {
45 success: false,
46 error: error.errors[0]?.message ?? "Invalid email"
47 };
48 }
49
50 return {
51 success: false,
52 error: "Failed to join waitlist. Please try again."
53 };
54 }
55}
56
57export async function getWaitlistStats() {
58 const [total, active] = await Promise.all([
59 db.waitlistEntry.count(),
60 db.waitlistEntry.count({ where: { isActive: true } }),
61 ]);
62
63 return { total, active, inactive: total - active };
64}

Waitlist Components

Basic Waitlist Signup

Simple, conversion-optimized signup form:

src/components/WaitlistSignup.tsx
1"use client";
2
3import { useState } from "react";
4import { Button } from "~/components/ui/button";
5import { addToWaitlist } from "~/server/actions/waitlist";
6
7export function WaitlistSignup() {
8 const [message, setMessage] = useState<string>("");
9 const [isLoading, setIsLoading] = useState(false);
10
11 async function handleSubmit(formData: FormData) {
12 setIsLoading(true);
13 setMessage("");
14
15 const result = await addToWaitlist(formData);
16
17 if (result.success) {
18 setMessage(result.message!);
19 // Reset form
20 const form = document.getElementById("waitlist-form") as HTMLFormElement;
21 form?.reset();
22 } else {
23 setMessage(result.error!);
24 }
25
26 setIsLoading(false);
27 }
28
29 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 <input
34 type="email"
35 name="email"
36 placeholder="Enter your email"
37 required
38 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>
45
46 {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}
Enhanced Waitlist Section

Full-featured section with social proof and benefits:

src/components/WaitlistSection.tsx
1"use client";
2
3import { useState, useEffect } from "react";
4import { WaitlistSignup } from "./WaitlistSignup";
5import { getWaitlistStats } from "~/server/actions/waitlist";
6
7export function WaitlistSection() {
8 const [stats, setStats] = useState({ total: 0, active: 0 });
9
10 useEffect(() => {
11 getWaitlistStats().then(setStats);
12 }, []);
13
14 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 Waitlist
19 </h2>
20
21 <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>
24
25 {/* 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 waitlist
30 </p>
31 </div>
32 )}
33
34 {/* Signup Form */}
35 <WaitlistSignup />
36
37 {/* 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 launch
44 </p>
45 </div>
46
47 <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 supporters
52 </p>
53 </div>
54
55 <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 progress
60 </p>
61 </div>
62 </div>
63 </div>
64 </section>
65 );
66}

API Management

Admin API Endpoints

RESTful API endpoints for waitlist management:

API endpoint examples
1// GET /api/waitlist/entries - Get all waitlist entries
2export async function GET() {
3 try {
4 const entries = await db.waitlistEntry.findMany({
5 orderBy: { createdAt: "desc" },
6 });
7
8 return Response.json({ entries });
9 } catch (error) {
10 return Response.json(
11 { error: "Failed to fetch entries" },
12 { status: 500 }
13 );
14 }
15}
16
17// POST /api/waitlist/activate - Bulk activate emails
18export async function POST(request: Request) {
19 const { emails } = await request.json();
20
21 await db.waitlistEntry.updateMany({
22 where: { email: { in: emails } },
23 data: { isActive: true },
24 });
25
26 return Response.json({ success: true });
27}
28
29// POST /api/waitlist/deactivate - Bulk deactivate emails
30export async function POST(request: Request) {
31 const { emails } = await request.json();
32
33 await db.waitlistEntry.updateMany({
34 where: { email: { in: emails } },
35 data: { isActive: false },
36 });
37
38 return Response.json({ success: true });
39}

Integration Examples

Landing Page Integration

Add waitlist to your landing page:

Landing page example
1// src/app/page.tsx
2import { WaitlistSection } from "~/components/WaitlistSection";
3
4export 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>
12
13 {/* Features */}
14 <section className="py-16">
15 {/* Feature content */}
16 </section>
17
18 {/* Waitlist CTA */}
19 <WaitlistSection />
20
21 {/* Footer */}
22 </main>
23 );
24}
Popup/Modal Integration

Trigger waitlist signup with exit-intent or timing:

Modal waitlist example
1"use client";
2
3import { useState, useEffect } from "react";
4import { Dialog, DialogContent, DialogHeader, DialogTitle } from "~/components/ui/dialog";
5import { WaitlistSignup } from "~/components/WaitlistSignup";
6
7export function WaitlistModal() {
8 const [isOpen, setIsOpen] = useState(false);
9
10 useEffect(() => {
11 // Show after 30 seconds or on exit intent
12 const timer = setTimeout(() => {
13 setIsOpen(true);
14 }, 30000);
15
16 const handleMouseLeave = (e: MouseEvent) => {
17 if (e.clientY <= 0) {
18 setIsOpen(true);
19 }
20 };
21
22 document.addEventListener("mouseleave", handleMouseLeave);
23
24 return () => {
25 clearTimeout(timer);
26 document.removeEventListener("mouseleave", handleMouseLeave);
27 };
28 }, []);
29
30 return (
31 <Dialog open={isOpen} onOpenChange={setIsOpen}>
32 <DialogContent>
33 <DialogHeader>
34 <DialogTitle>Don't Miss Out! 🚀</DialogTitle>
35 </DialogHeader>
36
37 <div className="space-y-4">
38 <p>
39 Join our waitlist to get early access and exclusive updates
40 about our launch.
41 </p>
42
43 <WaitlistSignup />
44
45 <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

Waitlist Email Templates

Send beautiful emails to your waitlist:

Email templates
1// Welcome email for new waitlist members
2export 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>
14
15 <p style="color: #4a5568; line-height: 1.6; margin-bottom: 30px;">
16 Thanks for joining our waitlist! You'll be among the first to know
17 when we launch and get exclusive early access.
18 </p>
19
20 <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>
28
29 <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}
37
38// Launch announcement email
39export 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>
46
47 <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>
50
51 <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 Now
56 </a>
57
58 <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}
💡 Waitlist Best Practices

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
📊 Analytics & Insights

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

Analytics tracking
1// Track waitlist signups
2posthog.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.