Netlify Forms looks like a two-line setup. Add data-netlify="true" to your form, push to deploy, done. That is what the docs suggest. That is not what happens when you are building a real site with JavaScript-rendered components, custom POST logic, and a URL structure Netlify quietly rewrites on its own. Balay ni Bruno & Co. hit five separate failure modes across client builds before we landed on a pattern that works every time. Here is exactly what broke and exactly how we fixed it.
Failure 1 and 2: Netlify Never Saw the Form, and the POST Went Nowhere
The first failure caught us completely off guard. Netlify detects forms by scanning your static HTML at build time. If your form lives inside a JavaScript component or gets injected into the DOM after the page loads, Netlify never registers it. Submissions return a 404 with no explanation. The fix is a hidden static HTML copy of the form that Netlify can find during the build scan, even if users never see it.
<!-- Hidden static form for Netlify to detect at build time -->
<form name="contact" netlify data-netlify="true" hidden>
<input type="text" name="name" />
<input type="email" name="email" />
<textarea name="message"></textarea>
</form>
The second failure hit us on the POST itself. Our JS was submitting to /contact.html as the form action. Netlify expects the action to be an empty string or a forward slash. Anything else and the submission either errors out or redirects incorrectly. Set action="" or action="/" and nothing else.
Netlify scans static HTML at deploy time, not at runtime. Any form your JavaScript renders after the page loads is invisible to Netlify's form detection. The hidden static copy is not a workaround. It is the required pattern.
Failure 3 and 4: Pretty URLs and Field Name Mismatches
The third failure was the most confusing because everything looked correct. Forms were being detected, the POST path was right, but submissions were silently dropping. The culprit was Netlify's Pretty URLs feature. When Pretty URLs is enabled, Netlify rewrites trailing slashes on URLs, and that rewrite intercepts form submissions before they reach the Forms handler. You have two options: disable Pretty URLs in your Netlify site settings, or add an explicit redirect rule in your netlify.toml to make sure form POST requests are not touched by the rewrite logic. We went with disabling it on the sites where forms were critical.
The fourth failure came down to field names. When you submit a form via JavaScript using fetch and a URL-encoded body, every field name in your JS payload must exactly match the name attributes in your hidden static HTML form. Netlify cross-references both. If your hidden form has name="email" but your JS is sending email_address, the submission gets dropped with no error. Here is the working JS pattern we use:
fetch("/", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
"form-name": "contact",
name: formData.name,
email: formData.email,
message: formData.message,
}).toString(),
});
Failure 5: Duplicate Form Names Cause Silent Conflicts
The fifth failure was the quietest one. We had two different forms on the same site, a contact form and a newsletter signup, and both had name="contact" in the hidden static HTML. Netlify uses the form name as the identifier for its submission inbox. Two forms sharing a name means submissions from both go into the same bucket, field mapping breaks, and some submissions vanish entirely. The fix is straightforward: every form on your site needs a unique name attribute. name="contact" for one, name="newsletter" for the other, and the JS form-name value must match the right one. Once we renamed them, both forms worked cleanly. Five failures, five fixes, and now we run through this checklist on every site before the first form goes live.
Key Takeaways
- Netlify forms must exist in static HTML at build time. JavaScript-injected forms are invisible to Netlify.
- A hidden static copy of the form in the HTML is the canonical fix for dynamic form detection.
- Form name attributes must be unique across the entire site, not just the page.
- Pretty URLs can silently break form submissions. Check it in netlify.toml.