Waitlist for a new SaaS6 min read
How to add a waitlist form to a Webflow site
Point a Webflow Form Block at Ikiform, tag signups by source with a hidden field, and keep Webflow's success state with a short script for every form.
Keep the Form Block you designed in Webflow and change its action to your Ikiform endpoint with the POST method. Add a hidden ref input and a honeypot in a Code Embed, then paste one site-wide script that posts every waitlist form with fetch, fills ref from the link and shows Webflow's own success block with the person's email.
Webflow is good at the page and fine at the form. Its native form handling stores submissions and emails you, but it caps how many it keeps by site plan and can't tag a signup with the campaign it came from. A custom action fixes both without leaving the Designer.
What's different on Webflow
- A custom action takes the form away from Webflow. The browser posts straight to Ikiform, so Webflow never sees the signup: it isn't in Webflow's form submissions, Webflow sends no email, and it uses none of your plan's form allowance.
- Webflow's success and error blocks stop working. They only appear when Webflow handled the post. Without a script, visitors land on Ikiform's thank-you page or your Redirect after submit URL.
- There's no hidden input element. Hidden fields and the honeypot go in a Code Embed inside the form, which needs a paid site plan, as does custom code.
- Nothing runs in the Designer. Custom actions and scripts only work on the published site, so test on your
webflow.iostaging domain.
- By default, the Form Block posts to Webflow, which stores the signup against your plan's allowance and shows the success block.
- With a custom action, the same Form Block points at Ikiform.
- The script stops the normal post, fills the ref field from the link and posts with fetch.
- Ikiform stores the signup, emails you and sends webhooks, then answers 201.
- The script hides the form and shows Webflow's success block with the person's email.
Set up the Form Block
- Select the form and open Settings. Remove Webflow and Email notifications under Send to, then add Custom action, enter
https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx, choose POST and save. - Select the email input and set its Name to
email. Webflow fills names from labels, so you'd otherwise getEmailorEmail 2, which Ikiform treats as a different field. - Delete any reCAPTCHA element. Ikiform doesn't verify it, and with a field definition its
g-recaptcha-responsevalue is refused as an unknown field. - Drag a Code Embed inside the form, next to the button, and paste the two inputs below.
- Set Redirect after submit in the Ikiform form's settings to a thank-you page you've published in Webflow. That's where people land if the script doesn't run.
<input type="hidden" name="ref" value="webflow">
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>Add the field definition on Ikiform's Fields tab with email required and ref as an optional text field with max_length 40. Every other name posted, such as a leftover Name input, then gets a 422, which shows you a mislabelled input on the first test.
The site-wide script
Paste this under Site settings › Custom code › Footer code, so it covers the hero form, the footer form and any page you add later. It only touches forms whose action starts with https://ikiform.com/f/.
<script>
const params = new URLSearchParams(location.search);
const ref = (params.get("ref") || params.get("utm_source") || "").toLowerCase().replace(/[^a-z0-9_-]/g, "").slice(0, 40);
const key = crypto.randomUUID();
document.querySelectorAll('form[action^="https://ikiform.com/f/"]').forEach((form, index) => {
const wrapper = form.closest(".w-form");
const done = wrapper?.querySelector(".w-form-done");
const fail = wrapper?.querySelector(".w-form-fail");
const button = form.querySelector('[type="submit"]');
const source = form.querySelector('[name="ref"]');
if (source && ref) source.value = ref;
form.addEventListener("submit", async (event) => {
event.preventDefault();
const label = button.value;
button.disabled = true;
button.value = button.dataset.wait || "Joining";
fail.style.display = "none";
try {
const response = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json", "Idempotency-Key": `${key}-${index}` },
body: new FormData(form),
});
const result = await response.json();
if (result.ok) {
const email = form.querySelector('[name="email"]').value;
const line = done.querySelector("div") ?? done;
line.textContent = `You're on the list. We'll write to ${email} on launch day.`;
form.style.display = "none";
done.style.display = "block";
return;
}
const wait = response.headers.get("Retry-After");
const message = wait ? `Lots of signups right now. Try again in ${wait} seconds.` : result.error.fields?.email?.[0] ?? result.error.message;
(fail.querySelector("div") ?? fail).textContent = message;
} catch {
(fail.querySelector("div") ?? fail).textContent = "Couldn't reach the server. Try again.";
}
fail.style.display = "block";
button.disabled = false;
button.value = label;
});
});
</script>?ref=or?utm_source=in the link fills every hiddenrefinput on the page. Without either, the value set in the embed is kept, so set it per form, such asheroandfooter, to see which one converts.- The idempotency key is made once per page load and suffixed per form. A double click stores one signup; a second signup on another form on the page still counts.
- Webflow's success block gets a line with the person's email, which catches typos: people notice
gmial.comwhen it's written back to them. - The button shows the Loading text you set in the Designer while the post is in flight.
- A
429shows how long to wait, from Ikiform'sRetry-Afterheader, instead of a generic failure.
fetch, so leave the form's spam check off. With the check on, Ikiform answers 403 challenge_required and the error block shows. If you need the check, skip the script; the plain post runs it with no code.Allowed domains
| Where the form runs | Add to Allowed domains |
|---|---|
| Staging | your-site.webflow.io |
| Your custom domain | example.com, which covers www.example.com |
| The Designer | Nothing. It never posts from there |
Never add webflow.io on its own; every Webflow site would match. The Webflow docs guide covers the same settings for a contact form, and the main guide shows how to forward signups to your email tool.
Check it works
- Publish to your
webflow.iostaging domain. - Under the form's Allowed domains, add your site's domain, plus
localhostwhile you develop. Leave it empty to accept posts from anywhere. - Open the page with
?ref=testand sign up. Webflow's success block shows your email, and the submission in Ikiform hasrefset totest. - Sign up again from the footer form with no query string.
refreads the value from that form's embed. - Turn off JavaScript and submit. You land on your Webflow thank-you page.
- Check the inbox of each notification recipient. Each gets an email with the fields, and Reply goes to the submitter when the form has an
emailfield. - Post with the honeypot filled in,
_gotcha=bot. It still gets a success answer, but the submission is stored as spam and sends no email or webhook.
Gotchas
| Problem | What to do |
|---|---|
404 not_found after submit | Method is GET, so the browser opened the endpoint like a link. Set it to POST and publish again. |
422 with Unknown field Email or g-recaptcha-response | An input's Name doesn't match the definition, or a reCAPTCHA element is left in the form. Rename the input to email or remove the element. |
| The script does nothing | You're in the Designer, the site has no paid plan, or Action has a space in it. Test on the published site. |
403 challenge_required | The form's spam check is set to Ikiform and a fetch post had no token. Get one with challenge(formId) from @ikiform/sdk before each submit, or turn the check off. |
403 origin_not_allowed, or every post lands in spam | The page's domain isn't in the form's Allowed domains. Add it, plus localhost for local tests. Subdomains of a listed domain are allowed. |
Questions
No. With a custom action the browser posts straight to Ikiform, so Webflow never receives the submission. It doesn't appear in Webflow's form submissions and doesn't use your plan's allowance.
Webflow has no hidden input element. Put a Code Embed inside the form with an input of type hidden. Code Embed needs a paid Site plan or a Core, Growth, Agency or Freelancer Workspace.
Yes, with a script. Webflow only shows its success block when it handled the post, so the script posts with fetch and shows the block itself, as in the sample above.
No. Ikiform emails you and your team, not the submitter, so there is no autoresponder or confirmation email. Send a webhook to your email tool and send the confirmation from there.
Stuck on a step, or want a guide for your situation? Email hello@ikiform.com.