September 20, 2026 · NBForms Team
Gatsby Contact Form: Why Netlify's Form Detection Misses It
Netlify Forms looks like the free, zero-setup answer on a Gatsby site — until Netlify's own docs explain exactly which forms its build system can't see, and what the workaround costs to keep working.
A Gatsby site ends up on Netlify often enough that Netlify Forms feels less like a choice and more like the default — it's already there, already free up to a point, and the docs make turning it on look like adding one attribute. That's true for a plain HTML page. It's a different story the moment the form in question is a React component, which on a Gatsby site is every form there is.
Photo by Jens Lelie on Unsplash
The bot that reads your build, once, and never again
Netlify's form detection isn't a running service watching a live site — it's a step in the build
itself. When a deploy finishes, Netlify's build system parses the finished HTML files looking for
a data-netlify="true" (or plain netlify) attribute on a <form> tag. Find one, and it strips
the attribute and quietly injects a hidden form-name input in its place before the files go
live. That's the entire mechanism — a one-time read of static files at the end of a build, not
anything watching requests afterward.
What Netlify's own documentation says about frameworks like Gatsby
Here's the line that matters, straight from Netlify's docs: "The Netlify build system finds your forms by parsing the HTML of your site when the build completes. This means that if you're using JavaScript to render a form client-side, our build system won't find it in the pre-built files." Their own setup guide lists React, Vue, Next.js, and Gatsby by name as frameworks this affects. It isn't a bug report or a workaround thread's theory about what's going wrong — it's Netlify stating plainly, in its own documentation, which forms its own feature can't see.
The workaround: a form that exists only for the bot to find
Netlify's documented fix is to give the build-time parser something plain to read, separate from
the real form a visitor interacts with — either a hidden static <form> (commonly dropped into
a file like public/__forms.html) carrying the netlify attribute and every field name, or a
hidden form-name input added directly to the JavaScript-rendered form so the two are linked
without a second file. Either version means the same thing operationally: a form that exists
purely so a build step can find something to register, doing nothing for any actual visitor.
Two forms, one name, and no warning if they drift
The condition for this working is that every field name in the real, rendered form matches a field name Netlify's parser already knows about — exactly, character for character. Add a field to the live form and forget the hidden counterpart, rename one without renaming the other, or typo either copy, and nothing announces the mismatch. No build error, no console warning, no failed request a browser's network tab would flag — the submission just doesn't show up where it's expected, and the first sign anything's wrong is usually someone asking why a form that looked fine in testing hasn't produced a single lead in two weeks.
A second bill, running under the first
Netlify Forms meters separately from whatever a site's hosting plan already covers. Accounts on Netlify's legacy pricing get roughly 100 submissions per site per month before overage charges apply; newer accounts (opened after September 2025) draw from Netlify's account-wide usage credits instead. File uploads are supported, capped at an 8 MB request size and a 30-second timeout per submission. None of this is unreasonable on its own — it's simply a second usage meter, on top of the hosting bill, that a form owner has to track independently of anything Gatsby itself does.
What leaving Netlify actually costs
The parsing step described above is specifically Netlify's build system reading Netlify's own deploy output. Move the exact same Gatsby site to Vercel, Cloudflare Pages, or a self-hosted static file server, and there's no equivalent feature waiting on the other end — every form depending on Netlify's detection stops working the moment the site stops deploying through Netlify, with no error at the point of migration to flag it. That's a fair trade for a site that will never leave, and a real cost for one that might.
The Gatsby page that never needed any of this
None of the above is a problem for a form that isn't asking a build step to find it in the first
place. Gatsby already renders every page to real, complete HTML at build time — that's the whole
premise of a static site generator — so a plain <form> posting to an external endpoint is
already sitting in the finished markup with nothing further required:
// src/pages/contact.js
export default function ContactPage() {
return (
<form action="https://api.nbforms.com" method="POST">
<input type="hidden" name="_token" value="YOUR_TOKEN" />
<label>Name</label>
<input type="text" name="name" required />
<label>Email</label>
<input type="email" name="email" required />
<label>Message</label>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
)
}
One file, nothing hidden
That's the whole form — one component, one visible <form>, no second copy anywhere for a build
step to cross-reference. The action attribute points at NBForms' endpoint directly, so the only
two parties in the exchange are the visitor's browser and that endpoint; nothing about which host
served the page ever enters into it, and nothing needs a matching counterpart kept in sync
somewhere else. A file field works the same way with no extra attribute — <input type="file" name="resume" /> inside the form is enough, since the browser sets the multipart encoding on its
own the instant a file input is present. Wanting a JavaScript-enhanced version with inline
success or error states instead of a page navigation is a reasonable next step, and works exactly
like the fetch-based pattern already covered for React forms without a server
— the Gatsby-specific part ends here, at a form that already works before any of that gets added.
Where Netlify Forms is still the right call
None of this makes Netlify Forms a bad feature — it's a genuinely zero-setup option for what it was built for: a form written as plain HTML with no client-side library rendering it, on a site with no plans to ever deploy anywhere else. That combination needs nothing beyond one attribute, and nothing here beats that for pure setup time. The moment either condition stops being true — the form becomes a React component, or the site's hosting decision becomes less than permanent — the hidden-form maintenance and the hosting lock-in are real costs worth weighing against an endpoint that was never tied to a specific host to begin with.
For the field-by-field markup this pattern needs in other frameworks, see the framework-specific snippets; for turning the same form into a page with nothing else around it, see hosted form pages.
Frequently asked questions
Does Netlify Forms just not work with Gatsby at all?
It can work, but not automatically — Netlify's own documentation names Gatsby specifically among the frameworks whose forms its build-time parser won't find without the hidden-form workaround added by hand, since that parser only reads pre-built HTML files, not anything rendered by JavaScript.
Does an endpoint-based form still work if the site ever leaves Netlify?
Yes, unchanged. The form posts straight to NBForms' endpoint regardless of which host is serving the static files, so switching from Netlify to Vercel, Cloudflare Pages, or anywhere else doesn't touch the form at all.
Do I need a data-netlify attribute or anything similar on the form?
No — that attribute only means something to Netlify's own build-time scanner. A form posting to an external endpoint has no build-time detection step to opt into in the first place.
Does a file upload field need a special enctype set by hand?
No. The moment a form contains an <input type="file">, the browser sets the multipart encoding on its own — nothing to configure in the Gatsby component itself.
Is Netlify Forms ever still the better call for a Gatsby site?
Yes, in one specific case: a form written as plain HTML with no client-side library involved, on a site that's never planning to leave Netlify. That combination needs no workaround at all, and nothing beats zero setup.