Signup forms · 4 fields
Waitlist form template
Collects interest before launch and tells you who is waiting and why. The open question about use is the valuable part: it shows what to build first and whom to invite first.
Waitlist
Live demo: try the fields. Submitting sends nothing.
02 / Field guide
Why these fields
Email
email · requiredRequired. It is how you invite people when access opens.
First name
text · optionalOptional, for a more personal invitation.
What would you use it for?
textarea · optionalOptional, and the field worth reading. One open question gets better answers than a list of personas.
Also send me occasional product news
checkbox · optionalOptional and unticked. Telling someone their access is ready is what they asked for; product news is a separate purpose that needs its own yes.
When to use it
Use it on a landing page before launch or for a feature in early access. For an ongoing newsletter, the newsletter signup template is shorter.
Promise only what you will do, such as one email when access opens. If you also want to send updates, ask with the separate checkbox.
Privacy and GDPR
- Joining a waitlist is a request for access, so you can email people about that access. A newsletter or other updates need the separate opt-in.
- To keep proof of the news opt-in, rename the checkbox to _consent and switch on consent capture in the form settings. Formward then stores the consent text and time with each signup.
- If the launch is cancelled or delayed for a long time, delete the list rather than keeping it for something else.
03 / Make it yours
Choose your stack. Copy your form.
Replace the example endpoint with your Formward endpoint, then adapt the fields to your needs.
<form action="https://forms.formward.eu/f/your-form-id" method="POST">
<label for="email">Email</label>
<input type="email" name="email" id="email" required />
<label for="name">First name</label>
<input type="text" name="name" id="name" />
<label for="use_case">What would you use it for?</label>
<textarea name="use_case" id="use_case"></textarea>
<label>
<input type="checkbox" name="news" id="news" aria-describedby="news-help" /> Also send me occasional product news
</label>
<small id="news-help">Optional. Without it we only email you when your access is ready.</small>
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px" aria-hidden="true" />
<button type="submit">Send</button>
</form>Accessibility
Accessible by default, and how to keep it that way
- Every field in the code has a visible label tied to it with for and id, so screen readers announce what to enter and a click on the label focuses the field.
- Required fields carry the required attribute, so the browser explains a missing answer before anything is sent. Say “required” in words as well; an asterisk or a colour alone is easy to miss.
- The spam trap field (_gotcha) is hidden from screen readers with aria-hidden and skipped by the Tab key, so people never meet it.
- In the JavaScript versions, show the confirmation or error in an element with role="status" so the result is announced, and keep the entered answers when sending fails.
Changed the fields? Paste your version into the HTML form tester to see exactly what it sends, or run it through the GDPR form checker.
04 / Implementation
How the code talks to Formward
Each form you create gets its own endpoint in the form https://forms.formward.eu/f/your-form-id. The HTML version posts to it directly and the browser is redirected to a thank-you page, so it works without JavaScript.
The Next.js version below sends the same fields with fetch and an Accept: application/json header. Formward then answers with { ok: true, id }, or an error body with 402 when the plan limit is reached and 429 when the rate limit kicks in. See responses and status codes.
Next.js client component
"use client";
import { useState } from "react";
export function WaitlistForm() {
const [sent, setSent] = useState(false);
async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const form = e.currentTarget;
const res = await fetch("https://forms.formward.eu/f/your-form-id", {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
if (res.ok) {
setSent(true);
form.reset();
}
}
if (sent) return <p>Thanks! Your message has been sent.</p>;
return (
<form onSubmit={onSubmit}>
<label htmlFor="email">Email</label>
<input type="email" name="email" id="email" required />
<label htmlFor="name">First name</label>
<input type="text" name="name" id="name" />
<label htmlFor="use_case">What would you use it for?</label>
<textarea name="use_case" id="use_case" />
<label>
<input type="checkbox" name="news" id="news" aria-describedby="news-help" /> Also send me occasional product news
</label>
<small id="news-help">Optional. Without it we only email you when your access is ready.</small>
<input
type="text"
name="_gotcha"
tabIndex={-1}
autoComplete="off"
style={{ position: "absolute", left: "-9999px" }}
aria-hidden="true"
/>
<button type="submit">Send</button>
</form>
);
}
Spam protection
- The generated code already includes the hidden _gotcha honeypot. Leave it in: a bot that fills it gets a normal success response, the submission is filed as spam and no email is sent.
- Every form has a per-IP rate limit, and you can restrict it to your own domains with the allowed-origins list in the form settings.
- Cloudflare Turnstile is optional and works on every plan, including Free. Turn it on in the form settings, add the widget to your markup and Formward checks the token server-side.
- The form settings can also reject disposable email addresses, which is worth turning on when a fake address would waste your time.
What happens after submit
- Each signup is emailed to you and stored in the EU on every plan. Export the list as CSV from Personal up to invite people in batches.
- From Professional, the autoresponder can confirm the place on the list. From Professional, a webhook can add each person to your product or mailing tool.
- The form's response settings take a submission limit and a closing date, if you want to cap the list.
Questions
- How do I see which channel brought people to the waitlist?
- Send the page's UTM parameters with the form as hidden fields; the lead generation page has a short script for it. Formward stores the source with each submission, and falls back to the referring site when there is no UTM source.
- How is this different from the newsletter signup template?
- A newsletter is ongoing and needs consent for marketing. A waitlist is a one-off request for access, plus an optional opt-in for news. Keeping them apart keeps your consent records honest.
Related
- Newsletter signup template
One field, plus a consent version.
- Lead generation
UTM source on every signup.
- Consent checkboxes
How the news opt-in should read.
- Next.js forms
The waitlist on a Next.js landing page.
Customize before publishing
- Remove fields you do not need and mark only essential answers as required.
- Replace placeholder wording with terms your audience already understands.
- Add your privacy notice, retention details and expected response time.
- Test keyboard focus, validation errors and the confirmation state on mobile.
Use this template
Create a free account and the form is set up from this template. Copy your endpoint and replace https://forms.formward.eu/f/your-form-id in the code above.