guide

How to generate an HTML form and handle everything it sends you

September 21, 2026 ・ Halict Editorial

Searching for an online html form generator usually means the form itself is not the hard part. The fields are already decided. What is not decided is where the submission goes, who finds out it arrived, and what happens to it after that. A generator solves about fifteen minutes of the problem and leaves the rest on the desk, which is fine as long as the rest is planned rather than discovered on the day the form goes live.

This walks through the whole path a submission travels: the markup, the destination, validation, spam, and the handling that starts once a response is sitting in front of a person. Most of the avoidable pain sits in the last two.

What a generator actually hands you

Three different products all call themselves form generators, and they stop at three different points.

Markup generators output a block of HTML, sometimes with matching CSS, for you to paste into a page. The output is static. It has no destination and no storage. The action attribute comes back empty, or set to #, or pointing at a placeholder you are expected to replace.

Endpoint services give you markup plus a URL to post it to. You keep the HTML in your own page, and the service accepts the POST, stores the submission, and emails you a copy. The form still looks and behaves like part of your site.

Hosted form tools give you no markup at all, or only an embed snippet. The form lives on their infrastructure, and the responses live there too.

The right choice follows from one question: is there a server you control that can accept a POST request and keep the result somewhere durable. If yes, a markup generator plus your own handler is the cheapest and most flexible path. If no, or if the answer is technically yes but nobody has time to maintain it, the decision is between an endpoint service and a hosted tool, and the rest of this article matters more than the markup does.

One thing worth settling early: generated markup is a starting point and not a deliverable. Every generator makes layout assumptions that will not match your site, and most of them produce a form that validates as HTML while quietly failing to send one or more fields.

The markup is the easy part, and generators still get three things wrong

Look for these before trusting generated output.

The name attribute

A field without a name attribute is never submitted. Browsers skip it entirely. Generators that identify fields only by id, which is common because id is what their own CSS hooks onto, produce forms that look finished and arrive at the server missing half their content. Check that every input, select, and textarea carries a name.

The enctype for file uploads

A form containing a file input needs enctype="multipart/form-data". Without it the browser sends the file name as a plain text value and never sends the file. Generators that let you drag a file field into the layout without changing the form attributes create exactly this failure, and it is invisible until someone asks why the attachment never arrived.

Labels and input types

A label tied to its field by for and id, or wrapping the field, is what screen readers announce and what makes the label itself a tap target on a phone. Separately, the type attribute changes the on screen keyboard on mobile: email brings up the at sign, tel brings up the number pad, date brings up a picker. The full list of input types is worth a read once, because using the right one removes more friction than any amount of styling.

Adding autocomplete tokens costs one attribute each and lets the browser fill in a name, an email address, a phone number, or a postal address from what it already holds.

<form action="/submit" method="post" enctype="multipart/form-data" accept-charset="utf-8">
  <label for="email">Email address</label>
  <input id="email" name="email" type="email" autocomplete="email" required>

  <label for="cv">Attach a CV</label>
  <input id="cv" name="cv" type="file" accept=".pdf,.doc,.docx">

  <button type="submit">Send</button>
</form>

Where the submission goes is the whole decision

The action attribute is the fork in the road. Four routes lead out of it, and they differ far more in what they leave you to build than in how hard they are to set up.

Route Who holds the data Set up cost What you still have to build
Your own endpoint You Highest Storage, notification, spam defence, retries, replies
A mailto action Nobody reliably Lowest Everything, and it breaks for many visitors
An endpoint service The service Low Reply handling, ownership, status tracking
A hosted form tool The tool Low Usually little, depending on the tool

The mailto: route deserves a specific warning because generators still offer it. Putting a mail address in the action hands the submission to whatever mail program is configured on the visitor's machine. If none is configured, which is normal on a work laptop that uses webmail and on most phones, nothing happens at all. The visitor sees no confirmation and no error. When it does work, the respondent sees the entire payload in a draft and can edit any of it before sending. Treat it as unusable for anything public.

Your own endpoint is the strongest option and the most honest about its cost. Accepting the POST is a few lines in any framework. What follows is the work: storing the submission so it survives a deploy, sending a notification that does not land in spam, deduplicating retries, and giving somebody a place to read it. The reference on sending and retrieving form data covers the mechanics well, and the mechanics are the short part.

Validation happens twice, and one of the two is not optional

Client side validation is the attributes the browser already understands: required, type="email", pattern, minlength, maxlength, min, max. These are worth using on every form because the error appears before the round trip, which measurably reduces the number of people who give up mid form. Adding novalidate to the form element turns the browser's own messages off so custom messages can take their place, which is the usual reason a generated form has a script attached to it.

None of that is security. Anything can post directly to the endpoint without loading the page, so every constraint has to be checked again on the server: required fields actually present, lengths bounded, numbers inside range, file types and sizes restricted, and unexpected fields ignored rather than trusted. The rule is to reject rather than coerce, because silently fixing a malformed submission produces a record nobody can explain later. The form validation guide sets out both layers.

Character encoding belongs in the same category. Set accept-charset="utf-8" on the form and make sure the handler decodes UTF-8. Names and addresses outside plain ASCII are the first thing to arrive mangled, and by the time anyone notices, the stored records are already wrong.

Spam arrives within days, and the cheapest defences are the best

A public form with no defence starts receiving automated submissions almost immediately. Four measures, roughly in order of value for effort:

A honeypot field. Add a field a human never sees, hidden with CSS rather than type="hidden", and reject any submission that fills it. Automated submitters fill every field they find. Real visitors never see it, so it costs nothing in completions.

A timing check. Record when the form was rendered and reject submissions completed in under a couple of seconds. Nobody types a real answer that fast.

Rate limiting at the endpoint. Cap submissions per address per minute. This also protects you from a badly behaved script rather than only from spam.

A captcha, last. It works, and it costs completions from exactly the people you want, plus it adds a third party dependency to your form. Reach for it when the first three have failed rather than first.

One related habit: never place submitted text straight into an HTML notification email without escaping it. A form that emails its own contents as HTML is a delivery mechanism for whatever someone typed into it.

The part no generator covers

Once the markup is right and the endpoint is up, the interesting failure moves somewhere else entirely. A working form produces a stream of messages, and nothing in the stack so far says who is responsible for each one.

The usual pattern is a notification email to a shared address, plus a spreadsheet somebody exports from time to time. Owner and status become hand maintained columns. Two people answer the same enquiry on the same morning, or an enquiry sits for a week because everyone assumed somebody else had it. When the respondent follows up, the reply that was sent is in one person's sent items and nowhere else, so reconstructing the conversation means asking around.

That cost never appears on an invoice, which is why it survives for years. At a hundred submissions a month it comfortably exceeds the price of any tool in this category. The question to ask of any setup is not whether it collects answers, since all of them do that, but whether a response carries an owner, a status, and a record of what was sent back. Tools differ enormously here, and it is the one axis that generators do not address at all, because the generator's job ended at the closing form tag.

If owning the endpoint is not worth it

For a form that has to work next week without a deploy, the hosted options are worth comparing by what they meter, because the meter decides when the tool breaks rather than what it costs on a quiet month.

Tally offers unlimited forms and submissions on its free tier under fair usage guidelines, with a 10 MB limit per uploaded file, and a Pro plan at 24 USD per month that removes the file size limit and allows unlimited members in shared workspaces. Typeform's Basic plan is 39 USD per month and includes unlimited forms, conditional logic, and 100 responses per month for one user. Tools metered by people rather than by responses take the opposite approach: unlimited forms and unlimited responses on every plan, with the price following how many colleagues need access, starting at nothing for a single person. Comparing what each plan includes against your busiest month rather than your average one is the only version of this exercise that tells you anything.

If the reason for generating markup was to keep the form inside your own page, check whether the tool supports that as well as its own hosted page, and check what happens on the response side once it arrives, since that is where the time goes.

What to change first

Start by confirming the two silent failures: every field has a name, and any form with a file input has multipart/form-data set. Those two account for most forms that appear to work and deliver nothing. Then decide who owns a response after it lands, and write that down before the form goes live rather than after the first thing gets missed. If the answer is more than one person, watch a response move from arrival to reply on one screen in Halict or any tool built the same way, and compare it against the spreadsheet you would otherwise be maintaining by hand.

Q1. Can an HTML form send an email without any backend?

Not reliably. Setting action="mailto:[email protected]" hands the submission to the mail program configured on the visitor's machine, and on a device with no configured mail client nothing happens at all. Sending email requires something server side, which means either your own handler, an endpoint service, or a hosted form tool.

Q2. Why is one of my form fields always empty when it arrives?

The field almost certainly has no name attribute. Browsers only submit fields that have one, and generated markup often identifies fields by id alone because that is what the accompanying CSS uses. Add name to every input, select, and textarea, then resubmit and check the payload.

Q3. Is the required attribute enough to validate a form?

It is enough to help an honest visitor and not enough for anything else. Client side attributes such as required and type="email" improve completion by showing the error before the round trip, but a request can be posted directly to the endpoint without ever loading the page. Every constraint has to be checked again on the server.

Q4. Do I need a captcha on a contact form?

Usually not as the first step. A hidden honeypot field plus a minimum time between render and submit removes most automated traffic and costs real visitors nothing, whereas a captcha reduces completions and adds a third party dependency. Add one only if the cheaper measures stop being enough.

Q5. Should the form live on my own page or on the tool's hosted page?

It depends on where people arrive. A form embedded in your own page keeps the visitor in one place and matches your styling, which matters for a contact page reached from your navigation. A hosted page needs no deploy and is easier to send as a link, which matters for an application or registration shared by email. Several tools offer both, so check that before choosing on this basis alone.

All guides

How to generate an HTML form and handle everything it sends you | Halict