Add a Waitlist to a Next.js App (3 Ways, ~5 Minutes)

The short answer
The fastest way to add a waitlist to a Next.js app is to point a plain HTML form at a hosted waitlist endpoint; no backend code at all. If you want the signup handled server-side (custom UI, position shown in your own page), use a server action or route handler with the waitlister SDK. All three patterns below are copy-paste and were verified against the live API.
Prerequisite: a waitlist. Two ways to get one:
- Dashboard: create one free at Waitlister; the waitlist key is on its Overview page (quickstart). That's all option 1 needs.
- From code: with an account API key (
wl_acct_…, Settings → API keys) you can create the waitlist programmatically and sign up subscribers with the same key — no dashboard step, works on every plan including free.
Building this with an AI coding agent? Hand it waitlister.me/skill.md (everything on this page in one fetchable file), or add the official MCP server, @waitlister/mcp, and the agent can create the waitlist, wire the form, and verify the signup itself. When we tested this with a fresh coding agent and only an account key, it had a live waitlist created in about three minutes and a verified real signup in about seven.
Option 1 — plain form action, no backend (any plan)
Point a form straight at the waitlist's form-action endpoint. Works in Next.js, plain React, or raw HTML; the subscriber is redirected to a hosted thank-you page showing their queue position and referral link.
// app/page.tsx — works as-is in a server or client component
export default function Home() {
return (
<form action="https://waitlister.me/s/YOUR_WAITLIST_KEY" method="POST">
<input type="email" name="email" placeholder="you@example.com" required />
<button type="submit">Join the waitlist</button>
</form>
)
}
One setup step: add your site's domain to the waitlist's whitelisted domains (waitlist → Settings), or submissions return 403. Optional fields: name, phone, referred_by; any other input name is stored as metadata.
Use this when you want zero server code and the hosted thank-you page is fine.
Option 2 — server action + SDK (App Router)
Keep users on your page and control the whole experience. Install the SDK:
npm install waitlister
Set your keys in .env.local (the SDK reads these names automatically):
# Account key (Settings → API keys) — works on every plan:
WAITLISTER_ACCOUNT_KEY=wl_acct_your-key
WAITLISTER_WAITLIST_KEY=your-waitlist-key
# Or the classic pair: a per-waitlist API key (waitlist Settings, Growth plan+)
# WAITLISTER_API_KEY=your-api-key
// app/actions.ts
'use server'
import { Waitlister } from 'waitlister'
const wl = new Waitlister() // picks up the env vars
export async function joinWaitlist(formData: FormData) {
const result = await wl.signUp({
email: formData.get('email') as string,
name: (formData.get('name') as string) || undefined
})
return {
position: result.position,
referralCode: result.referral_code
}
}
Wire it to a client component and show the position on success. Two behaviors worth knowing: signups are idempotent per email (re-submitting returns the existing position, not an error), and the key never reaches the browser because the action runs server-side.
Plan notes: with an account key, signUp works on every plan, including free (with lower rate limits). A per-waitlist API key needs the Growth plan; on a plan without API access the SDK throws a typed PlanError rather than failing silently. The zero-key fallback still exists: signUpViaForm from the same package uses the public form endpoint and returns the hosted thank-you page URL to redirect() to.
Create the waitlist from code
If you don't have a waitlist yet, an account key lets the SDK create one. Useful in setup scripts, CLIs, and agent workflows:
import { Waitlister } from 'waitlister'
const wl = new Waitlister({ accountKey: process.env.WAITLISTER_ACCOUNT_KEY })
const { waitlist } = await wl.waitlists.create({ name: 'My Product' })
waitlist.key // use as WAITLISTER_WAITLIST_KEY
waitlist.form_action_url // option 1's form target — signups work immediately
Works on every plan, within your plan's waitlist cap. Waitlist names and URL slugs are unique across all of Waitlister, so a taken name is rejected with a message telling you what to change. One key then covers everything: wl.waitlist(waitlist.key).signUp(…), .stats(), and (on Growth+) API subscriber management.
Show a live signup count
Social proof for your landing page: the stats endpoint works with an account key on every plan, and its counters update within about ten seconds of a signup:
const { subscribers_total } = await wl.waitlist(key).stats()
// → "Join 1,204 people waiting"
Option 3 — REST route handler (no SDK, or Pages Router)
The same thing with a route handler and plain fetch, if you'd rather not add a dependency:
// app/api/waitlist/route.ts
export async function POST(req: Request) {
const { email, name } = await req.json()
const res = await fetch(
`https://waitlister.me/api/v1/waitlist/${process.env.WAITLISTER_WAITLIST_KEY}/sign-up`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': process.env.WAITLISTER_ACCOUNT_KEY! // or a per-waitlist API key
},
body: JSON.stringify({ email, name })
}
)
const data = await res.json()
return Response.json(data, { status: res.status })
}
Auth is the X-Api-Key header (not a Bearer token) and accepts either key type — account keys work on every waitlist you own. The full request/response schema, including the account-scoped POST /api/v1/waitlists for creating waitlists, is in the machine-readable OpenAPI spec; the human version is in the API reference.
Verify it works
- Submit a real email address. Deliverability is validated at signup, so fake test addresses get rejected with a 400.
- Expect
success: truewith aposition(options 2–3) or a redirect to the thank-you page (option 1). - Submit the same email again: you should get
is_new_sign_up: falsewith the same position. That confirms the signup persisted.
FAQ
Can I add a waitlist to Next.js without a backend?
Yes. Point any form's action at https://waitlister.me/s/{your-waitlist-key} (option 1). No API key, no server code; it works on static exports and every plan, as long as your domain is whitelisted in the waitlist settings.
Can I create the waitlist itself from code?
Yes. wl.waitlists.create({ name }) with an account key (wl_acct_…, Settings → API keys) returns the waitlist key and a working form URL in one call. It works on every plan within your plan's waitlist cap. The same key signs up subscribers and reads stats; managing the subscriber list via the API needs Growth or higher.
Can my AI coding agent set this up for me?
Yes. Give the agent an account key plus waitlister.me/skill.md, or install @waitlister/mcp in your MCP client. The API returns actionable error messages (plan caps, taken names, auth mistakes) that agents can self-correct from. In our test, a fresh agent with only an account key created the waitlist in about three minutes and had a verified real signup in about seven.
Does this work with plain React or Vite apps?
Options 1 and 3 do, unchanged: option 1 is plain HTML, and option 3's route handler can live in any backend. Option 2's server action is Next.js App Router-specific.
How do subscribers see their queue position?
Option 1 redirects them to a hosted thank-you page with their position and referral link. Options 2–3 return position and referral_code in the response, so you render it however you want — or use the returned redirect_url to send them to the hosted thank-you page.
How do referrals work in a custom integration?
Every signup gets a referral_code. Put it in your share links as ?ref=<code>, then pass incoming codes back as referredBy (SDK), metadata.referred_by (REST), or referred_by/ref (form fields). Points, position changes, and fraud detection are handled for you. Details: referral program.
