Launch

Building a waitlist with referrals in Next.js

Collect emails, issue a referral code, and compute a queue position with Prisma in a Next.js server action.

2 min read

A referral waitlist needs three facts per signup: the email, a code they can share, and the code that brought them in. The Waitlist / Launch Kit stores those on one Prisma model and uses SQLite.

Schema

model Signup {
  id         String   @id @default(cuid())
  email      String   @unique
  refCode    String   @unique
  referredBy String?
  createdAt  DateTime @default(now())
}

model Settings {
  id       Int     @id @default(1)
  launched Boolean @default(false)
}

referredBy is the referrer’s refCode, not a foreign key. A bad or missing ref is stored as null. The kit does not walk a referral tree or pay anyone.

Join action

const referredBy = String(formData.get("ref") || "") || null;
const refCode = crypto.randomBytes(4).toString("hex");

randomBytes(4) is 8 hex characters. The page passes the current ?ref= query into a hidden input, so the server action sees it without reading the URL again. The email is unique. Signing up twice with the same address should hit that constraint; handle the Prisma error and send the person back to their existing code instead of showing a 500.

After insert, the action redirects to /?code= plus the new refCode.

Position

Position is not a stored rank. The page loads the signup by code and counts rows created earlier:

const ahead = await prisma.signup.count({
  where: { createdAt: { lt: mine.createdAt } }
});
const position = (ahead || 0) + 1;

The share link is https://{host}/?ref={refCode}, where host comes from the request headers. Two signups in the same millisecond can share a timestamp; the count is then a tie, not a stable order. That is acceptable for a launch page. Do not promise a locked rank unless you store an integer sequence.

Launch flag and export

Settings.launched flips the heading between “Get in early.” and “We are live.” The admin page lists signups and links to /api/export, which returns CSV columns email,refCode,referredBy. Protect that route with the admin token the kit already checks. A public export is a mailing list.

The public page also shows signup.count() as “N people on the list.” That count includes everyone, not only people referred by the current visitor.

Want to skip the setup? The Waitlist / Launch Kit already includes the form, referral codes, position, CSV export, and the launch toggle.

Product updates for developers shipping with Designyff kits.

Purchased source may be used in personal, commercial, and client projects. Do not redistribute or resell the original source. Terms of Service

Designyff © 2026 | Starter kits for developers | Powered by  Tacko