How to make a form in HTML
A form is a <form> element holding fields and a submit button. Each field needs a name: that is the key the value is sent under. Fill in the form and press Send. The preview does not send anything anywhere; it shows exactly what the browser would have sent.
The pieces:
actionis the URL the data goes to, andmethodis how (posthere).nameon every field. Its value is what the reader typed.<label>names each field on screen and for screen readers (see HTML label).requiredandtype="email"make the browser check the fields before sending. Try sending with an empty name, or withabcas the email.- The submit button sends the form. Pressing Enter in a text field does too.
action and method: where and how the data goes
| Attribute | Values | Default |
|---|---|---|
action | Any URL | The current page's URL |
method | get, post, dialog | get |
enctype | application/x-www-form-urlencoded, multipart/form-data, text/plain | application/x-www-form-urlencoded |
With method="get", the fields become the query string of the URL: a search form with name="q" goes to /search?q=blue+shoes. That is right for searches and filters, because the result can be bookmarked and shared. With method="post", the fields go in the body of the request and do not appear in the address bar or the history. Use POST for anything that logs in, creates, changes or deletes something, and for passwords.
A form with a file upload (<input type="file">) needs method="post" and enctype="multipart/form-data", or only the file's name is sent. method="dialog" is for a form inside a <dialog>: submitting closes the dialog instead of sending a request.
What gets sent
Only fields with a name are sent, and only when they are not disabled. Press Submit and compare the fields with what the form sends:
The form sends city, zip, source and action. The field with no name, the disabled field and the unchecked checkbox are left out. Hidden inputs are sent but never shown, and the submit button sends its own name and value because it was the button used.
Grouping fields with fieldset and legend
<fieldset> draws a box around related fields, and <legend> is its caption. Screen readers announce the legend when focus enters the group, which is what makes a set of radio buttons like the ones below understandable: "Delivery, Standard, radio button".
disabled on a fieldset disables every field inside it at once, and none of them are sent. Remove it and the gift fields become usable.
Built-in validation: required, pattern and friends
Browsers check these attributes before submitting and show a message on the first field that fails:
| Attribute | Checks |
|---|---|
required | The field is not empty (a checkbox is checked) |
type="email", type="url" | The value looks like an email address or URL |
minlength, maxlength | Text length |
min, max, step | Number and date range |
pattern | The whole value matches a regular expression |
:invalid and :valid style fields by their state. :user-invalid is usually better: it applies only after the reader has changed the field and moved on, or tried to submit, so an empty form does not open covered in red.
Three things about pattern trip people up. It must match the whole value (no ^ or $ needed). It only runs when the field is not empty, so pair it with required. And the browser's error message is generic, so describe the format in visible text next to the field. Chrome and Firefox add the title text to their message, but it is easy to miss.
To turn the checks off, for example while you test your server's validation, add novalidate to the <form>. A single submit button can skip them with formnovalidate.
Browser validation is for the reader's convenience. Anyone can edit the page or send a request without it, so the server must check every value again.
Custom messages with setCustomValidity
When a rule cannot be written as an attribute, such as "the two passwords must match", set the message from JavaScript. A field with a non-empty custom message is invalid; an empty string makes it valid again:
The repeat field has no name, so only one copy of the password is sent.
Handling the submit in JavaScript
To send the form without leaving the page, listen for submit, call preventDefault(), and read the fields with FormData. Listening for submit rather than a button's click catches every way of submitting, including Enter, and runs only after the built-in validation has passed:
Object.fromEntries(new FormData(form)) turns the fields into a plain object. For fields that can have several values (checkboxes sharing a name, a multiple select) use formData.getAll('name') instead, because the object keeps only the last value.
Common mistakes
- Fields without
name. They are silently left out of the submitted data. - Placeholders instead of labels. The hint disappears as soon as the reader types. Use a
<label>. - A
<button>with notypeused for something other than submitting. Inside a form it is a submit button. - A form inside a form. Nesting forms is invalid; the browser ignores the inner
<form>tag. - Listening for
clickon the submit button instead ofsubmiton the form. Enter key submissions are missed. - Trusting browser validation on the server side. Validate again there.
- GET for passwords. They end up in the URL, the history and server logs.
Frequently Asked Questions
How do you create a form in HTML?
Wrap the fields in <form action="/signup" method="post">, give every field a name and a <label>, and end with <button type="submit">. When the form is submitted, the browser sends each name=value pair to the action URL.
What is the difference between GET and POST in a form?
method="get" (the default) puts the fields in the URL, as in /search?q=shoes, which suits searches and filters that should be bookmarkable. method="post" sends them in the request body, which is what you want for logins, sign-ups and anything that changes data.
Why is my form field not being submitted?
It has no name attribute, or it is disabled. Only named, enabled fields are sent. An unchecked checkbox or radio button is also left out entirely.
What are fieldset and legend used for?
<fieldset> groups related fields and <legend> is the group's caption. Screen readers announce the legend when focus enters the group, which is what makes a group of radio buttons understandable, and disabled on a fieldset disables every field in it.
How do I validate a form in HTML without JavaScript?
Use attributes: required, type="email", minlength, maxlength, min, max and pattern. The browser blocks the submit and shows a message for the first invalid field. Always validate on the server too, because these checks run in the reader's browser and can be bypassed.
How do I stop a form from reloading the page?
Listen for the form's submit event and call event.preventDefault(), then send the data yourself with fetch: form.addEventListener('submit', (e) => { e.preventDefault(); fetch(form.action, { method: 'POST', body: new FormData(form) }); }).