Creating Forms Isn't as Simple as It Looks
People think making a form is just dropping input fields on a page and calling it done. That works until you try to handle it at scale. I spent three days debugging a registration form last year that looked perfect on desktop. Mobile Safari was autofilling the wrong field in the name box because the for="id"> attribute didn't match the input . The form submitted successfully, but half the usernames had "firstname.lastname" concatenated together. Took me a while to realize it wasn't a code issue—it was a browser heuristic fighting against sloppy markup. Start with the HTML structure. Not the styling. The actual semantic markup. Use
<form action="/submit" method="POST"> <fieldset> <legend>Contact Information</legend> <label for="email">Email Address</label> <input type="email" id="email" name="email" required> <label for="message">Message</label> <textarea id="message" name="message" rows="5"></textarea> <button type="submit">Submit</button> </fieldset> </form> The attribute is the most overlooked part. Every input needs a matching label. Screen reader users navigate forms by label. Browsers use it for autofill heuristics. If you skip it, you're not just being inconsiderate—you're breaking functionality for real people. Then there's validation. HTML5 gives you built-in validators with attributes like , , , , , and . These are useful. They're also inconsistent across browsers. Chrome will show a red border and a tooltip. Firefox shows something different. Safari on iOS might not show anything at all unless you add JavaScript. Don't rely solely on client-side validation. Server-side validation is non-negotiable. I learned that the hard way when a form accepted a 50,000-character message field because I forgot to set a and my backend didn't sanitize input. The database row went to 4MB. Support ticket took two weeks to resolve.
For styling, CSS has some form-specific properties worth knowing. can be styled, but Safari sometimes ignores opacity changes on placeholders. and are notoriously hard to restyle consistently across browsers. The standard workaround is wrapping them in a and hiding the actual input with or , then styling the label instead. It's a bit hacky but it works everywhere.
Get the Full Details
Learn More
How To Create A Form Using Forms at Lily Howchin blog
Common Mistakes I See People Make
Using instead of or . Browser keyboards change based on input type. On mobile, brings up the @ symbol. brings up a number pad. allows letters on some Android keyboards because the spec is vague. This matters for user experience more than anything else. Nesting tags inside
without closing them properly. Or wrapping the entire form in a and then putting another
inside that. HTML5 is fine with block-level elements inside forms, but you need to keep your nesting logical.
must wrap or reference the input, not sit as a sibling without a connection. Another thing: submitting forms with JavaScript without handling the default behavior. If you attach an to a button and call , that's fine. But if you accidentally trigger a form submit while also having an AJAX handler, you'll get duplicate submissions. I've seen production forms send data twice because a developer added a fetch call but forgot to prevent the native submit. The database ended up with duplicate entries and the user got charged twice.
When to Use a Library vs. Building From Scratch
For simple contact forms, building from scratch takes about 15 minutes. You know exactly what's happening. There are no dependencies. No version conflicts. For complex forms with conditional logic, dynamic fields, or multi-step flows, a library like Formik (React), Vuelidate (Vue), or even a headless UI component library saves significant time. But they add bundle size and learning curve. If your form has more than five fields or requires conditional validation rules, consider whether a library actually saves time or just adds complexity. I once worked on a project where the team used a form builder that generated 400 lines of JavaScript for a 12-field form. The page load took four seconds on 3G. Switching to vanilla HTML with minimal JavaScript cut the payload by 85% and the form still handled everything the builder did. The only thing the builder handled better was initial accessibility testing, but that was a one-time cost we could verify manually.
Testing Your Form Before It Goes Live
Don't just test in Chrome. Test in Safari, Firefox, and on an actual phone if possible. Autofill behaves differently. Keyboard types differ. Some browsers interpret differently than others. Test with a screen reader if you can—VoiceOver on Mac or TalkBack on Android. Both will tell you immediately if your labels are broken. Also test the submission path. Does the server receive the data in the expected format? Are there rate limits? What happens if someone submits the form ten times in rapid succession? Add a simple loading state to the button. Disable it after the first click. It prevents duplicate submissions and gives the user feedback that something is happening. Forms are one of those things that seem trivial until they fail in production. Get the basics right first—semantic HTML, proper labels, client and server validation—and then worry about the polish. Most people do it backwards.
How To Create A Form Or Survey Using An Easy Form Builder | Steps To ...