# Nextjs Waitlist Page: Countdown, Empfehlungen, echte Zahlen

> Bau eine Nextjs Waitlist Page mit Countdown, E-Mail-Feld, Platz auf der Liste und Empfehlungslink auf Postgres, dazu eine Anmeldezahl, die nie gefälscht ist.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 16. Okt. 2026
- Kategorie: [Praxis: Screens und Apps](https://wingo-ui.com/de/blog/category/build)
- Lesezeit: 10 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/nextjs-waitlist-page
- Englische Fassung: https://wingo-ui.com/blog/nextjs-waitlist-page

## Kurz gesagt

Eine Next.js Waitlist Page braucht fünf Teile: einen Countdown bis zum Launch, ein einziges E-Mail-Feld, den Platz der Person auf der Liste, einen Empfehlungslink, der sie nach vorn bringt, und eine Anmeldezahl, die nur erscheint, wenn sie echt ist. Den Screen baust du mit der Vorlage Waitlist Page von Wingo UI, die Anmeldungen speicherst du über eine Server Action in einer einzigen Postgres-Tabelle. Position und Empfehlungscode kommen dann aus deiner Datenbank statt aus den Beispielzahlen der Vorlage.

Eine Nextjs Waitlist Page ist oft die erste Seite, die ein Produkt hat, und sie geht schnell mit den Beispielzahlen der Vorlage online. Die Defaults unserer eigenen Vorlage zeigen eine Zeile „Join 4,812 people“, eine Position, die einfach die Zahl plus eins ist, und einen Empfehlungscode, den keine Datenbank kennt. Dieses Tutorial baut eine Pre-Launch-Seite in Next.js 16 mit einem Countdown bis zum Launch, einer E-Mail-Anmeldung, dem echten Platz der Person auf der Liste, einem Empfehlungslink, der sie nach vorn bringt, und einer Anmeldezahl, die nur erscheint, wenn sie stimmt.

## Was braucht eine Warteliste-Landingpage?

Eine Warteliste-Landingpage braucht fünf Teile, denn sie hat nur eine Aufgabe: die E-Mail-Adresse von jemandem bekommen, der interessiert ist, und ihm einen Grund geben, einen Freund mitzubringen.

1. **Ein Launch-Datum oder ein klares „bald verfügbar“.** Ein Countdown funktioniert, wenn du ein Datum hast, das du auch hältst. Ohne Datum schreibst du „bald verfügbar“ und lässt den Timer weg.
2. **Ein einziges E-Mail-Feld.** Jedes weitere Feld ist eine Sache mehr, die vor der Belohnung ausgefüllt werden muss. Mehr als ein optionales Select für die Rolle würden wir nicht ergänzen.
3. **Ein Platz auf der Liste nach der Anmeldung.** „Du bist #213“ ist die Belohnung für das Eintippen der E-Mail und gibt dem Empfehlungslink einen Zweck.
4. **Ein Empfehlungslink, den man mit einem Tipp kopiert und teilt.** Jede Anmeldung bekommt einen eigenen Code; Freunde, die sich damit eintragen, bringen die empfehlende Person nach vorn.
5. **Social Proof nur, wenn er echt ist.** Der [Pre-Launch-Waitlist-Guide von KickoffLabs](https://kickofflabs.com/blog/pre-launch-waitlist-guide/) empfiehlt, die Zahl zu zeigen, und sagt, dass kleine Zahlen schaden: Null und zehn fühlen sich beide schlecht an. Als Größe, ab der eine Zahl FOMO auslöst, nennt er 500 oder mehr (geprüft am 16. Oktober 2026). Genau dieser Druck bringt gefälschte Zahlen in die Produktion. Die Lösung: Lies die Zahl aus der Datenbank und blende sie aus, bis sie es wert ist, gezeigt zu werden.

Wenn du lieber einen Next.js-Starter als eine Komponentenbibliothek willst, findest du bei Vercel [Waitly](https://vercel.com/templates/next.js/waitly). Waitly speichert Anmeldungen in Notion, begrenzt die Rate mit Upstash Redis und verschickt E-Mails über Resend; sein Notion-Schema enthält Felder für den Empfehlungscode und die empfehlende Person (geprüft am 16. Oktober 2026). Dieser Beitrag geht den anderen Weg: ein fertiger Screen aus einer Komponentenbibliothek und ein Speicher, der dir gehört.

## Wie installiere ich die Next.js-Waitlist-Vorlage?

Wingo UI liefert den Screen als [Vorlage Waitlist Page](https://wingo-ui.com/templates/waitlist-page): den Countdown, das E-Mail-Formular, die Erfolgskarte mit Position und Empfehlungslink, Feature-Teaser und ein FAQ. Falls du nach „nextjs waitlist template“ gesucht hast: Das hier ist eins im Sinne von shadcn. Der Quellcode wird in dein Projekt kopiert, du kannst also jede Zeile ändern.

```bash
npx wingo-ui@latest add waitlist-page
```

Das fügt `components/templates/waitlist-page.tsx` hinzu, dazu die Datei mit den Beispieldaten und die Teile, die die Vorlage nutzt: den [Countdown](https://wingo-ui.com/components/countdown), den [Copy Button](https://wingo-ui.com/components/copy-button) für den Empfehlungslink, das [Accordion](https://wingo-ui.com/components/accordion) für das FAQ, das [Select](https://wingo-ui.com/components/select) für die optionale Rolle, außerdem Marketing-Header, Footer und die Section-Blöcke.

Probier den Ablauf aus, bevor du irgendetwas verdrahtest. Die Demo unten nutzt ein simuliertes Backend, das 900 ms wartet und eine Position zurückgibt.

> Live-Demo (Waitlist Page): Gib eine E-Mail ein und drück Join the waitlist: Der Button dreht sich, dann wird aus dem Formular dein Platz auf der Liste mit einem Empfehlungslink zum Kopieren und Teilen. Eine ungültige Adresse zeigt zuerst einen Fehler direkt am Feld. Probier sie unter [Waitlist Page](https://wingo-ui.com/templates/waitlist-page) aus und installier sie mit `npx wingo-ui@latest add waitlist-page`.

Die Props, auf die es in diesem Tutorial ankommt, alle aus dem Quellcode der Vorlage:

| Prop | Default | Was es macht |
|---|---|---|
| `launchAt` | `"2026-11-17T17:00:00.000Z"` | Ziel des Countdowns und Datum in der Pill; `null` ergibt eine Seite „bald verfügbar“ |
| `count` | `4812` | Die Zeile „Join N people“ und die Fallback-Position (count + 1) |
| `people` | sechs Beispielnamen | Der Stapel mit Initialen neben der Zahl |
| `onJoin` | wartet 900 ms | Bekommt `{ email, role }`; darf `{ position, referralCode }` zurückgeben; ein abgelehntes Promise zeigt den Formularfehler |
| `referralUrl` | `https://acme.com/mobile?ref=` | Der Link zum Teilen ist dieser Wert plus der Code |
| `spotsPerReferral` | `10` | Die Zahl in „Move up 10 spots for every friend who joins“ |
| `roles` | sechs Beispielrollen | Optionen des optionalen Rollen-Selects; `[]` blendet es aus |
| `showSocials` | `true` | Das Band „Follow the build“ mit Beispiel-Links zu sozialen Netzwerken |
| `labels`, `features`, `faqs` | Acme-Beispieltexte | Jeder sichtbare Text |

Achte auf die Defaults für `count` und `people`. Es gibt sie, damit der Screen in einer Vorschau fertig aussieht. Wenn du sie auslieferst, behauptet deine Seite 4.812 Anmeldungen, die du nicht hast. Der Rest dieses Tutorials ersetzt beide durch echte Daten.

## Wie speichere ich Anmeldungen und berechne den Platz auf der Liste?

Eine einzige Postgres-Tabelle reicht für die Daten. Der Empfehlungsbonus steht in einer winzigen gemeinsamen Datei, weil sowohl die Abfrage als auch der Text auf der Seite ihn brauchen:

```ts
// app/waitlist/config.ts
export const SPOTS_PER_REFERRAL = 10
```

Die Tabelle hält den Rest. Jede Zeile hat die E-Mail, eine optionale Rolle, einen zufälligen Empfehlungscode und den Code der Person, die empfohlen hat.

```sql
create table waitlist (
  id bigserial primary key,
  email text not null unique,
  role text,
  referral_code text not null unique,
  referred_by text references waitlist (referral_code),
  created_at timestamptz not null default now()
);

create index waitlist_referred_by on waitlist (referred_by);
```

Die Regel für die Position: Dein Score ist deine Anmeldereihenfolge minus 10 für jede Person, die mit deinem Code beigetreten ist. Deine Position ist eins plus die Zahl der Leute mit niedrigerem Score, bei Gleichstand gewinnt, wer sich früher angemeldet hat. Die Reihenfolge kommt aus `row_number()` und nicht direkt aus `id`: Ein `insert ... on conflict` verbraucht auch dann einen Wert der Sequenz, wenn es nur aktualisiert. Wiederholte Anmeldungen hinterlassen also Lücken in `id`, und durch eine Lücke wäre ein Bonus von 10 Plätzen weniger als 10 Personen wert. Diese Skizze nutzt den Client [postgres](https://github.com/porsager/postgres) (`npm install postgres`); jeder Treiber, der SQL ausführt, funktioniert genauso.

```ts
// app/waitlist/waitlist-db.ts
import "server-only"
import { randomBytes } from "node:crypto"
import postgres from "postgres"

import { SPOTS_PER_REFERRAL } from "./config"

const sql = postgres(process.env.DATABASE_URL!)

export async function waitlistCount() {
  const [row] = await sql`select count(*)::int as count from waitlist`
  return row.count as number
}

async function positionOf(code: string) {
  const [row] = await sql`
    with scored as (
      select w.id, w.referral_code,
        row_number() over (order by w.id) - ${SPOTS_PER_REFERRAL} * (
          select count(*) from waitlist r where r.referred_by = w.referral_code
        ) as score
      from waitlist w
    )
    select count(*)::int + 1 as position
    from scored s
    join scored me on me.referral_code = ${code}
    where s.score < me.score or (s.score = me.score and s.id < me.id)`
  return row.position as number
}

export async function addToWaitlist(input: { email: string; role?: string; ref?: string }) {
  // eine empfehlung zählt nur, wenn der code existiert
  const [referrer] = input.ref
    ? await sql`select referral_code from waitlist where referral_code = ${input.ref}`
    : []
  // wer zweimal beitritt, bekommt die erste zeile zurück: gleicher code, gleiche empfehlung
  const [row] = await sql`
    insert into waitlist (email, role, referral_code, referred_by)
    values (
      ${input.email},
      ${input.role ?? null},
      ${randomBytes(6).toString("hex")},
      ${referrer?.referral_code ?? null}
    )
    on conflict (email) do update set email = excluded.email
    returning referral_code`
  const referralCode = row.referral_code as string
  return { referralCode, position: await positionOf(referralCode) }
}
```

Zwei Details tragen hier das meiste Gewicht. Die Klausel `on conflict` sorgt dafür, dass eine wiederholte Anmeldung den ursprünglichen Code zurückgibt statt eines Fehlers; wer zweimal beitritt, sieht also denselben Link. Und weil die empfehlende Person nur beim ersten Insert gespeichert wird, ändert eine erneute Anmeldung über deinen eigenen Link nicht, wer dich empfohlen hat. Die Vorlage exportiert auch einen Helper `referralCode(email)`, der aus der Adresse einen kurzen Code ableitet. Für Vorschauen taugt das als Fallback, aber einen zufälligen Code aus der Datenbank kann niemand aus einer E-Mail-Adresse erraten.

Jede Abfrage der Position berechnet den Score für die ganze Tabelle. Das ist günstig, solange die Liste klein ist und `referred_by` einen Index hat. Wenn sie wächst, leg eine Zählerspalte `referrals` an, erhöh sie, sobald eine empfohlene Anmeldung eintrifft, und berechne den Score aus dieser Spalte statt aus der Unterabfrage.

## Wie verbinde ich das Formular mit einer Server Action?

Die Vorlage ist eine Client-Komponente, also teilt sich die Seite in zwei Teile: eine Server Component, die Daten liest, und einen kleinen Client-Wrapper, der die Vorlage rendert. Das Speichern läuft über eine Server Action, die die E-Mail prüft und genau die Form zurückgibt, die `onJoin` erwartet.

```ts
// app/waitlist/actions.ts
"use server"

import { addToWaitlist } from "./waitlist-db"

const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/

export async function joinWaitlist(entry: { email: string; role?: string }, ref?: string) {
  const email = entry.email.trim().toLowerCase()
  if (email.length > 254 || !EMAIL.test(email)) throw new Error("Ungültige E-Mail-Adresse")
  return addToWaitlist({ email, role: entry.role?.slice(0, 40), ref })
}
```

Wirft die Action einen Fehler, wird das Promise abgelehnt und das Formular zeigt seine Meldung „That didn't work“. Wird es erfüllt, zeigt die Erfolgskarte die Position und baut den Link zum Teilen aus `referralUrl` plus dem zurückgegebenen Code.

Die Seite selbst ist eine Server Component und liest den Code `ref` aus der URL und die echte Zahl. In Next.js 16 ist `searchParams` ein Promise, und sobald du es liest, rendert die Seite pro Request. Genau das willst du hier ohnehin, weil sich die Zahl ändert.

```tsx
// app/waitlist/page.tsx
import type { Metadata } from "next"
import { Waitlist } from "./waitlist"
import { waitlistCount } from "./waitlist-db"

export const metadata: Metadata = {
  title: "Warteliste für Acme Notes",
  description: "Acme Notes startet am 1. Dezember. Trag dich auf die Warteliste ein und hol dir eine Einladung.",
}

export default async function WaitlistRoute({ searchParams }: PageProps<"/waitlist">) {
  const { ref } = await searchParams
  const count = await waitlistCount()
  return <Waitlist count={count} referredBy={typeof ref === "string" ? ref : undefined} />
}
```

## Sollte eine Waitlist Page die Anmeldezahl zeigen?

Zeig sie nur, wenn sie echt ist und groß genug, um zu helfen. Übergib die echte Zahl aus der Datenbank und übergib `0`, bis sie eine Schwelle überschreitet, die du festlegst: Die Vorlage rendert die Zeile „Join N people“ und den Stapel mit Initialen nur, wenn `count` größer als null ist. Der Wrapper unten nutzt 500, die Größe, die der Guide von KickoffLabs nennt. Übergib in jedem Fall `people={[]}`: Das sind Beispielnamen, und für die Initialen echter Menschen brauchst du deren Erlaubnis.

```tsx
// app/waitlist/waitlist.tsx
"use client"

import Link from "next/link"
import { WaitlistPage } from "@/components/templates/waitlist-page"
import { joinWaitlist } from "./actions"
import { SPOTS_PER_REFERRAL } from "./config"

// darunter schadet eine kleine zahl, also bleibt die zeile verborgen
const MIN_SOCIAL_PROOF = 500

export function Waitlist({ count, referredBy }: { count: number; referredBy?: string }) {
  return (
    <WaitlistPage
      linkComponent={Link}
      launchAt="2026-12-01T17:00:00Z"
      count={count >= MIN_SOCIAL_PROOF ? count : 0}
      people={[]}
      roles={[]}
      spotsPerReferral={SPOTS_PER_REFERRAL}
      referralUrl="https://example.com/waitlist?ref="
      onJoin={(entry) => joinWaitlist(entry, referredBy)}
      showSocials={false}
      labels={{
        pillSoon: "Acme Notes · Bald verfügbar",
        pillLaunch: (date) => `Acme Notes · Start am ${date}`,
        title: "Notizen, die sich selbst sortieren",
        description: "Schreib, wo du willst. Acme Notes legt es ab, verknüpft es und findet es wieder. Trag dich auf die Warteliste ein und hol dir eine frühe Einladung.",
        countdownDone: "Acme Notes ist offen. Die Einladungen gehen jetzt raus.",
        shareText: "Ich stehe jetzt auf der Warteliste von Acme Notes.",
        emailSubject: "Komm mit auf die Warteliste von Acme Notes",
      }}
      features={[
        { title: "Erst schreiben", description: "Öffne die App und tipp los. Ordner und Tags kommen später oder nie." },
        { title: "Von Anfang an verknüpft", description: "Erwähne eine Notiz und Acme verlinkt in beide Richtungen." },
        { title: "Suche, die mitliest", description: "Finde eine Notiz an dem, was drinsteht, nicht an ihrem Namen." },
      ]}
      faqs={[
        { question: "Wann startet Acme Notes?", answer: "Am 1. Dezember 2026. Die Einladungen gehen in der Reihenfolge der Liste raus." },
        { question: "Wie komme ich nach vorn?", answer: `Jeder Freund, der über deinen Link beitritt, bringt dich ${SPOTS_PER_REFERRAL} Plätze nach vorn.` },
        { question: "Kann ich die Liste wieder verlassen?", answer: "Jederzeit, über den Link in jeder E-Mail, die wir dir schicken." },
      ]}
    />
  )
}
```

Die Konstante kommt aus `config.ts` und nicht aus `waitlist-db.ts`, denn wenn du die Datenbank-Datei in eine Client-Komponente importierst, ziehst du den Postgres-Treiber und `DATABASE_URL` Richtung Browser-Bundle. Die Zeile `import "server-only"` oben in `waitlist-db.ts` macht aus diesem Fehler einen Build-Fehler statt einer Überraschung zur Laufzeit; Next.js behandelt den Import selbst, das Paket `server-only` zu installieren ist optional.

Halte FAQ und Erfolgskarte auf dem Stand deiner tatsächlichen Regeln. Das Beispiel-FAQ der Vorlage verspricht 10 Plätze pro Empfehlung; wenn deine Abfrage 5 nutzt, müssen Prop und Text beide 5 sagen. Ersetz vor dem Launch jeden Beispieltext in `labels`, `features` und `faqs`, auch die Pill: Die Vorlage formatiert das Launch-Datum in der Zeitzone von New York. Startest du um Mitternacht in Europa, schreib das Datum in `pillLaunch` selbst, statt das übergebene Argument zu nutzen.

Das ist der ganze Ablauf einer Referral-Warteliste in React: Jemand öffnet `/waitlist?ref=3f9a1c2b7d04`, die Seite liest auf dem Server den Code, das Formular schickt ihn mit der E-Mail mit, und die neue Zeile schreibt die Empfehlung bei der nächsten Abfrage der Position gut.

## Wie baue ich einen Launch-Countdown in Next.js ein?

Übergib den Launch-Zeitpunkt als ISO-String an `launchAt`, und die Vorlage reicht ihn an die Countdown-Komponente weiter. Die übernimmt für dich das Ticken, die Zeit bis zur Hydration und das Ende. Mit der Zeit lässt der Countdown führende Einheiten weg, die auf null stehen (Tage, solange der Launch weit weg ist, in der letzten Stunde nur noch Minuten und Sekunden), tickt an den Grenzen der Einheiten und zeigt bei null die Meldung aus `countdownDone`. Bis der Client nach der Hydration die Uhrzeit kennt, rendern die Ziffern als `--` in ihrer endgültigen Breite, also verschiebt sich nichts. Renderst du die Seite für Screenshots oder brauchst du festes Server-HTML, legt das Prop `now` die Uhrzeit fest.

Dieselbe Komponente kannst du auch außerhalb der Seite nutzen, zum Beispiel in einer Ankündigungsleiste auf der Hauptseite. Die Variante `text` schreibt die verbleibende Zeit in Worten mitten in einen Satz:

```tsx
import { Countdown } from "@/components/ui/countdown"

export function LaunchBar() {
  return (
    <p className="text-sm text-muted-foreground">
      Acme Notes startet{" "}
      <Countdown to="2026-12-01T17:00:00Z" variant="text" completedContent="heute" />
    </p>
  )
}
```

Screenreader hören nicht jeden Tick; die Komponente aktualisiert einmal pro Minute einen Satz und kündigt das Ende einmal an.

> Live-Demo (Countdown): Wähl mit den Buttons unter der Karte, wann der Launch ist: In 10 Sekunden tickt er herunter und zeigt die Meldung zum Ende, in 45 Tagen zeigt er Tage, und ein Datum in der Vergangenheit rendert sofort den fertigen Zustand. Probier sie unter [Countdown](https://wingo-ui.com/components/countdown) aus und installier sie mit `npx wingo-ui@latest add countdown`.

## Was sollte ich ergänzen, bevor ich den Link zur Warteliste teile?

Ergänz ein Rate Limit, eine E-Mail-Bestätigung und einen Weg, die Liste zu verlassen. Die Seite funktioniert an diesem Punkt, aber diese drei Dinge gehören auf den Server, bevor der Link rausgeht:

- **Rate Limit.** Eine Server Action ist ein öffentlicher Endpoint. Begrenz sie pro IP, so wie jedes Anmeldeformular; Waitly nutzt dafür Upstash Redis.
- **E-Mail-Bestätigung.** Schick einen Bestätigungslink und zähl eine Empfehlung erst, wenn die empfohlene Person bestätigt hat. Sonst klettert ein Skript mit falschen Adressen auf Platz 1. Ergänz eine Spalte `confirmed_at` und filtere die Zählung der Empfehlungen danach.
- **Abmelden und Löschen.** Wenn dein FAQ verspricht, dass man die Liste jederzeit verlassen kann, bau diesen Weg vor dem Launch.

Auf Smartphones erledigt die Vorlage die mobile Arbeit schon selbst: Behältst du das Rollen-Select, öffnet es sich unter 768px als Bottom Sheet mit seinem Label als Titel, und mit `layout="split"` rutscht das Smartphone-Mockup unter 1024px unter den Text. Kommt der Launch-Tag, kann die [Vorlage Launch Page](https://wingo-ui.com/templates/launch-page) dieselbe URL mit ihrer Live-Phase übernehmen, und die [Vorlage Referral Page](https://wingo-ui.com/templates/referral-page) deckt ein dauerhaftes Empfehlungsprogramm ab, wenn die Warteliste geschlossen ist.

Weitere Screens, die so gebaut sind, findest du in der Kategorie [Praxis: Screens und Apps](https://wingo-ui.com/de/blog/category/build), angefangen mit dem [Tutorial zum Next.js Admin Dashboard](https://wingo-ui.com/de/blog/nextjs-admin-dashboard-tutorial) (auf Englisch). Sobald die Leute drin sind, übernimmt der [Guide zum React Onboarding Flow](https://wingo-ui.com/de/blog/react-onboarding-flow) den ersten Start, und das [Tutorial zur shadcn Pricing Page](https://wingo-ui.com/de/blog/shadcn-pricing-page) behandelt die Seite, die sie sehen, wenn die kostenlose Einladung endet.

## Wie geht es weiter?

Installier die Vorlage mit `npx wingo-ui@latest add waitlist-page`, tausch die Beispieltexte und -daten gegen deine eigenen und verbinde die Server Action mit deiner Datenbank. Waitlist Page und Countdown gehören zu Wingo UI Pro, das $8 im Monat, $80 im Jahr oder einmalig $150 kostet, Stand Oktober 2026 ([Preise](https://wingo-ui.com/de/pricing)); Copy Button, Accordion und Select, die die Vorlage nutzt, sind kostenlos.

## Komponenten in diesem Artikel

- [Waitlist Page](https://wingo-ui.com/templates/waitlist-page) (Pro): React waitlist page template: a launch countdown, an email signup with social proof, your place in line with a referral link and FAQ. Installation: `npx wingo-ui@latest add waitlist-page`
- [Countdown](https://wingo-ui.com/components/countdown) (Pro): A React countdown timer to a launch, sale or deadline, shown as rolling digit blocks, inline in a sentence or in words, with done content. Installation: `npx wingo-ui@latest add countdown`

## Häufige Fragen

### Wie baue ich eine Waitlist Page in Next.js?

Render die Seite aus einer Server Component, die die echte Anmeldezahl und den ?ref=-Code liest, gib beides an eine Client-Komponente mit dem Formular weiter und speichere jede E-Mail über eine Server Action, die Position und Empfehlungscode zurückgibt. Die Vorlage Waitlist Page von Wingo UI liefert den Screen, den Speicher bringst du mit.

### Wie berechnet eine Referral-Warteliste den Platz auf der Liste?

Eine einfache Regel ist die Reihenfolge der Anmeldung minus ein Bonus pro Empfehlung: Jeder Freund, der sich über deinen Link einträgt, zieht eine feste Zahl an Plätzen von deinem Score ab, und deine Position ist eins plus die Zahl der Leute mit niedrigerem Score. Eine einzige Postgres-Abfrage mit row_number() und einer Zählung der Empfehlungen berechnet das.

### Sollte eine Waitlist Page zeigen, wie viele sich angemeldet haben?

Nur wenn die Zahl echt ist. Lies sie aus deiner Datenbank und blende die Zeile aus, bis sie groß genug ist, um etwas zu bedeuten; die Vorlage Waitlist Page versteckt die Zahl und den Avatar-Stapel, wenn du count={0} übergibst.

### Ist die Waitlist-Vorlage von Wingo UI kostenlos?

Nein. Die Vorlage Waitlist Page und der Countdown gehören zu Wingo UI Pro, während die Teile, auf denen sie aufbaut, etwa Copy Button, Accordion, Button und Select, kostenlos sind. Alles installierst du mit npx wingo-ui@latest add.

### Wie verhindere ich, dass jemand eine Referral-Warteliste austrickst?

Mach die E-Mail-Spalte eindeutig, lass eine zweite Anmeldung nie ändern, wer jemanden empfohlen hat, setz ein Rate Limit auf die Action und zähl eine Empfehlung erst, wenn die neue Person ihre E-Mail bestätigt hat. Ein paar falsche Adressen kommen trotzdem durch, versprich die Einladungen also nicht streng nach Position.

---

Quelle: https://wingo-ui.com/de/blog/nextjs-waitlist-page
