Add a waitlist form to any HTML website
Three ways to add a LaunchList form to a hand-written HTML site: the inline embed, enhancing your own form, or a native form that needs no JavaScript at all.
A hand-written site gives you the most choice, because nothing is stripping your markup. Pick by how much control you want.
Option A: the inline embed#
Least work. The form takes the design you pick in the form editor, under Design, and updates when you publish a change to it.
<!doctype html>
<html lang="en">
<head>
<script
src="https://embed.getlaunchlist.com/v1/embed.js"
async
></script>
</head>
<body>
<!-- frm_8Kd2Qm4Rp9 is an example ID; use your own from Embed & API → Your website -->
<div data-launchlist-form="frm_8Kd2Qm4Rp9"></div>
</body>
</html>
The script goes in the <head> once. The <div> can appear as many times as you like.
Option B: enhance your own form#
Keep your markup and your CSS. Add one attribute and we take over the behavior: submission, live format validation, a one-tap fix for email domain typos, inline errors, referral attribution and success routing. The one thing this path does not do is the on-blur deliverability check, which is only in our own rendered form. Everything the server enforces at submit is identical either way.
<form
data-launchlist-enhance="frm_8Kd2Qm4Rp9"
action="https://embed.getlaunchlist.com/f/frm_8Kd2Qm4Rp9/submit"
method="POST"
>
<label for="email">Email</label>
<input id="email" type="email" name="email" required />
<input type="hidden" name="_channel" value="html" />
<button type="submit">Join the waitlist</button>
</form>
Match each input's name to the field key from your form editor so its value lands in that field
and is validated. For a simple waitlist the identity input must be name="email", or the signup
cannot be attributed. The loader still goes in the <head> once.
A survey on your own site asks for an email too, so its answers belong to someone: give your survey
form an email input, required. The status page never asks for it, because the person answering
there has already signed up.
If the loader script doesn't load, the form still posts to LaunchList and the signup is saved, like Option C. With Message chosen under After signup, that fallback returns the visitor to your page without the message.
This is the right choice when the form belongs to a designed page and an isolated embed would look like a transplant.
Option C: no JavaScript at all#
A native form posting straight to the capture endpoint. The browser submits it, our server redirects to your Status page or a URL you choose.
<form
action="https://embed.getlaunchlist.com/f/frm_8Kd2Qm4Rp9/submit"
method="POST"
>
<input type="email" name="email" required />
<input type="hidden" name="_channel" value="html" />
<button type="submit">Join</button>
</form>
Nothing to load, nothing to break. This works with JavaScript disabled, behind strict content security policies, and inside templates that strip scripts.
Copy the action from Embed & API → Your website rather than typing it. It points at the embed host,
the same origin the loader script comes from, and the two matching is what makes the next tip work.
Reading the answer yourself#
If your own script posts to the capture endpoint with Accept: application/json, a signup shown in
place answers with success.heading and success.message. Both are already filled in for that person
and in their language, so a {{first_name|there}} in your message arrives as their name, or as
"there". Show them as they are. Someone who was already on the list gets one neutral line instead,
and nothing about their place in line.
CAPTCHA#
If your project has CAPTCHA set to Test or On, every option that runs the loader shows it. Options A
and B draw the challenge for you, and so does Option C when the loader is on the page. In your own
form (B, or C with the loader) it goes just above your submit button, or under the row that holds it.
To place it yourself, add an empty element with data-launchlist-captcha inside the form:
<form
data-launchlist-enhance="frm_8Kd2Qm4Rp9"
action="https://embed.getlaunchlist.com/f/frm_8Kd2Qm4Rp9/submit"
method="POST"
>
<input type="email" name="email" required />
<input type="hidden" name="_channel" value="html" />
<div data-launchlist-captcha></div>
<button type="submit">Join the waitlist</button>
</form>
Option C without the loader cannot show a challenge. Those signups still go through; they just arrive unverified, which counts as a small risk signal rather than a refusal.
Your CAPTCHA key only works on hostnames you list in your provider account, so add the domain this form lives on there too.
Content Security Policy#
If your site sets a CSP, Options A and B need the embed origin allowed. The runtime injects its own stylesheet into each shadow root and loads the product typeface from that origin:
script-src 'self' https://embed.getlaunchlist.com;
connect-src 'self' https://embed.getlaunchlist.com;
style-src 'self' 'unsafe-inline';
font-src 'self' https://embed.getlaunchlist.com;
style-src 'unsafe-inline' is what lets the embed style itself. With Option A those styles live
inside the shadow root and cannot reach the rest of your page. Option B adds one small stylesheet to
your page head, defining .launchlist-error and .launchlist-suggestion so its inline error lines
are legible; override either class from your own CSS.
With CAPTCHA on, also allow your provider's script and frame host: challenges.cloudflare.com for
Turnstile, www.google.com and www.gstatic.com for reCAPTCHA, or js.hcaptcha.com and
hcaptcha.com for hCaptcha, in both script-src and frame-src.
If your form's success message shows an image, add an img-src entry for wherever that image is
hosted, or the message renders without it.
If your form uses a font pairing picked under Design (or, on a form made before designs, a brand typeface other than the default DM Sans), the embed loads it from Google Fonts, so add those two hosts as well:
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://embed.getlaunchlist.com https://fonts.gstatic.com;
Option C needs no CSP change at all, since it loads nothing.
Adding social proof#
A live counter beside the form usually lifts conversion more than any copy change:
<div
data-launchlist-widget="counter"
data-project="acme"
data-label="founders already in"
></div>
Counter, leaderboard, milestone and countdown are all available. They show aggregate numbers only, never anybody's details.
If something is not working#
| What you see | What to do |
|---|---|
| Nothing renders | Check the script tag loaded, and that the form's public ID is right. Errors are logged to the browser console. |
| A field lands in the wrong place | To fill a specific form field, its input name must match that field's key, not its label. Inputs the form does not define are kept as Input data columns, not lost. |
| Referrals never credit | On a native form, load the script so it can inject the referral code, or send ref yourself. If the script is already loaded, check the form's action is on the same host the script came from: it only claims forms on its own origin. |
| A no-JS submit goes nowhere useful | The form's success behavior is Message, which gives the server no URL to redirect to. In the form editor, under After signup, switch it to Status page or Link (on the Collect plan and above), then publish. |
| Styles look wrong | The embed is in a shadow root and ignores your CSS by design. Restyle it in the form editor, under Design. |
Using the classic LaunchList dashboard#
A waitlist made in the classic LaunchList dashboard has its own snippet: a <div class="launchlist-widget"> with widget.js in the <head>, a form posting to getlaunchlist.com/s/ with its form key (with widget-diy.js), or the popup form with popup.js. That snippet
keeps working for now and sends signups to the classic dashboard's waitlist, not to a project here.
A classic form key does not work with the snippet on this page.
To bring that waitlist here, import it into a project, then replace the classic snippet on your HTML site with the one from Embed & API → Your website. Your signups are copied, and the classic waitlist is not changed.
Next: every way to integrate · the REST API
Common questions
How do I add a waitlist form to a static HTML site?
Add one script tag to the head and one div where the form should appear. The form renders itself, isolated from your CSS, and updates when you change its fields in LaunchList.
Can I use a waitlist form without any JavaScript?
Yes. Write a normal HTML form posting to the capture endpoint. The browser submits it and our server redirects to your result page, so it works with JavaScript disabled entirely.
How do I keep my own form styling?
Use the enhance method. Add data-launchlist-enhance to your existing form and we take over submission, validation, errors and referrals while your markup and CSS stay untouched.