You found an HTML form generator, picked name, email and message, copied the code into your page and pressed Send. The page did something, maybe it reloaded or showed an error, and no message ever reached your inbox.
On 2 November we went through every site on Google's first page for "html form generator" and "contact form generator", 16 sites in total, and asked each one for that same three-field contact form. Nine wouldn't show any code without an account. The other seven gave us code, and we pasted each one into an empty page without changing a character and pressed Send with test data. None of them delivered the message.
The generators aren't broken. A form in HTML only collects the fields. Something on a server has to receive the POST and email it to you, and most generators leave that part to you without saying so loudly. Below is what each one actually hands you, and what you need to add.
We make Formgong, a form backend, and our generator was measured the same way. Its row is in the table.
9 of the 16 show no code until you sign up
Jotform, 123FormBuilder, Google Forms, Zoho Forms, forms.app, POWR, Canva, landing-page.io and Tally all send you to an account before you get anything you can paste. Tally lets you build without one and asks for an account when you press Publish. That's a fair model: the form lives on their servers, and you embed it or link to it. It's just not what most people mean by "generate HTML".
The 7 that give you code, and where the data goes
Our own generator is in the last row for comparison.
| Generator |
action in the copied code |
What happens when the visitor presses Send |
|---|---|---|
| Paperform |
# (the same page) |
A POST to your own page; on a static host that's usually an error |
| Basin | A sentence telling you to get an endpoint | The browser treats the sentence as a path on your site and gets a 404 |
| HelpDesk | A sentence telling you to add an endpoint | Same as Basin: a 404 on your own site |
| BeautifyTools | No <form> at all, and the button is type="button"
|
Nothing |
| html-css-js.com |
action_page.php, method GET, new tab |
A 404 for action_page.php in a new tab |
| Formcarry | https://formcarry.com/s/YOUR-FORM-ID |
Formcarry's "Form is invalid" page |
| MyFormConnect | https://myformconnect.io/f/<form_uuid> |
Raw JSON on screen: {"error":"Form not found"}
|
| Formgong |
https://formgong.com/submit with fk_your_access_key
|
"That access key does not exist." shown under the form |
Formcarry, Basin, MyFormConnect, HelpDesk and Formgong all work the same way: the generator gives you the form, and their service receives it once you put your own form ID or key in place of the placeholder. We didn't open new accounts for this test, so we only checked the copied code before that step.
The MyFormConnect row is worth a closer look. Its code includes a script meant to show a friendly message, but the class names still contain the literal text <form_uuid>. That makes querySelector throw, the script never runs, and the visitor gets raw JSON. So when a generator's placeholder appears in more places than the action, replace it everywhere.
What else was missing from the copied code
We ran the 7 snippets through axe-core and checked a few things a contact form needs:
- Spam protection: none of the 7 had any. No honeypot, no CAPTCHA in any default copy. Formgong's copy adds a hidden honeypot field.
-
autocomplete: none of the 7 set it. Without it, browsers and password managers can't fill in the visitor's name and email, which matters on a phone. Formgong's copy sets it on name and email. -
Accessibility errors: 2 of 7. html-css-js.com produced fields with no label (3 critical axe violations). Basin's default Send button has white text on
#007bff, a contrast of 3.98:1, under the 4.5:1 minimum. -
Required fields: 3 of 7 marked none as
required. The form will happily send an empty message.
How to make a generated form actually send
Whichever generator you used, the fix comes down to the same few checks. We hit every one of them while running this test.
Point the action at something that takes forms. GitHub Pages, Netlify, Vercel and a plain HTML upload don't run PHP, so action_page.php has nothing behind it. You need a form backend URL or a route on your own server. Before you publish, search the pasted code for YOUR, <form_uuid>, fk_your_ and action_page. In our run, every one of those strings was still sitting in somebody's copy.
Give every field a name. The browser leaves out any field without one. It looks normal on the page, the visitor types into it, and it never reaches you.
<!-- sent -->
<input type="email" id="email" name="email" autocomplete="email" required>
<!-- looks the same, never sent -->
<input type="email" id="email">
Say thank you only when the server agrees. fetch doesn't reject on a 404 or a 401. If your code does .then(() => showThanks()), the visitor gets a thank-you for a message that went nowhere, and you'll never hear about it. Read the response first:
form.addEventListener("submit", async (event) => {
event.preventDefault();
try {
const res = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
const data = await res.json().catch(() => ({}));
if (res.ok && data.success === true) showThanks();
else showError("That didn't go through. Please email us instead.");
} catch {
showError("No connection. Please try again.");
}
});
success is the field our backend returns. Yours may call it something else, so send one good and one broken submission and look at both answers.
Send yourself a message before anyone else does. Open DevTools on the Network tab, submit, and click the request. A POST to your backend with a 2xx status is what you want, followed by the email. If the request went to your own domain, the action is still a placeholder. This takes a minute, and it would have caught all 7 copies in our test.
How the test was run
We searched Google on 2 November 2026 in a fresh browser, signed out, with hl=en&gl=us, and took the organic links from the first page of both queries. That gave 16 different sites (Jotform and Formcarry show up for both). On each one we picked the contact preset where there was one, otherwise added Name, Email and Message and left everything else at its default, then copied the default HTML. Every copy went into the same blank page and got one submission with made-up data while a script logged the requests. For accessibility we used axe-core 4.10.2 with the WCAG 2.1 A and AA rules. It's one day's results, and rankings move. We didn't go past the sign-up wall on the 9 tools that have one, so we can't say anything about their forms.
If you'd rather start from a form that already has the honeypot, autocomplete, labels and the success check, our free form generator exports HTML, Tailwind, Bootstrap, React and Vue. You paste your access key from a free account and it sends.
Did a generated form ever fail on you in a way that isn't on this list? Tell us in the comments. We'd like to add the cases we missed.

Top comments (0)