Wow. I haven’t blogged in 7 months again. A lot of stuff is happening, but I’m going to focus on explaining something that I finally took the time to understand.
Back in 2021, I wrote this post about using Netlify to handle contact forms on a website. The gist is that you could set up a contact form on your website & not have to build out a backend to make it work. That process could be delegated to Netlify. I used it to add working contact forms to HTML sites, React sites & Next.js sites.
To make it work for React & Next.js sites, we had to add a hidden 2nd copy of the form to the /public folder using HTML. I never really understood why.
Last night, I helped someone to get their contact form working on their portfolio using Netlify. Her site was built using React & the form wasn’t working. I looked at some React sites that I built & found that the big difference between mine & hers was that I had that HTML version of the form in the /public folder, as well as having the “real” one in React. She got her contact form to work, but I didn’t remember *why* the static HTML version needed to be there, so I researched.
React Renders at Runtime, Netlify Reads at Build Time
React apps are rendered dynamically. The content of each “page” is created by JavaScript in the browser at runtime. When your React app is built, the output doesn’t contain actual HTML for your form – it contains a skeletal HTML file (index.html) and some JavaScript. That JavaScript dynamically creates everything you see.
Netlify’s form handling, on the other hand, is set up by scanning the static HTML files in your site at build time. It doesn’t run your JavaScript. So if your form is only created by React, Netlify can’t see it. It doesn’t exist yet.
Add a Hidden HTML “Dummy” Form
To fix this, we give Netlify a static version of the form that it can see at build time:
- Create an HTML form with the same
nameand field names as your React form - Put it in your /
publicfolder (either inindex.htmlor a separate file) - Mark it as
hiddenso it never appears to users
<form name="contact" netlify netlify-honeypot="bot-field" hidden>
<input name="name" type="text" />
<input name="email" type="email" />
<input name='phone' type='tel'/>
<textarea name="message"></textarea>
</form>
That’s it. This dummy form gives Netlify everything it needs to register and configure the form submission endpoint on their servers.
The React Form Is the One Users Actually Use
When your app runs in the browser, your React-rendered form is the one users actually see & interact with:
<form name="contact" method="POST" netlify">
<input type="hidden" name="form-name" value="contact" />
<input name="name" type="text" required />
<input name="email" type="email" required />
<input name='phone' type='tel'/>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
This form sends a POST request to Netlify with the correct form-name and field names. Netlify doesn’t care that it wasn’t part of the static build. As long as the data matches what it expects, it accepts the submission.
Think of the Dummy Form as a Body Double
The hidden HTML form is a body double. Its just there so that Netlify has a form name & field names to register the form. The real one in React does the actual work. That’s the one that the users see. Because users never interact with the dummy form & it never gets submitted, there’s no conflict with the React form, even though they have the same name & fields. Only the React one should include a submit button & have form logic.
This setup was strange to me at first. I didn’t quite get why we needed the HTML version of the form in a React site. Now that I understand build time vs runtime, it makes sense. It gives Netlify a static page to work with while users interact with the React interface. Hopefully, it clears things up for some of you who are seeing this for the first time.