What a Form Actually Does
A form is how a web page collects information from a visitor and sends it somewhere. Every login screen, search box, registration page and contact form on the web is an HTML form underneath. It is the point where your page stops being a document you read and becomes something you interact with.
The important thing to understand first is what HTML is responsible for and what it is not. HTML builds the fields, labels them, groups them, and packages up the values when the visitor submits. It then hands that package to a URL you specify. What happens next — saving to a database, sending an email, checking a password — is entirely the job of server-side code written in PHP, Python, Node.js or something similar. HTML alone cannot store or process anything.
The <form> element wraps the whole thing and carries two attributes that decide where the data goes and how it travels: action and method. Every input the visitor fills in must be inside that form element, or its value simply will not be sent.
<form action="/register" method="post">
<label for="fullname">Full name</label>
<input type="text" id="fullname" name="fullname" required>
<label for="email">Email address</label>
<input type="email" id="email" name="email" required>
<label for="roll">Roll number</label>
<input type="text" id="roll" name="roll">
<button type="submit">Register</button>
</form> action and method: GET or POST
action is the URL the data is sent to. If you leave it out or set it to an empty string, the form submits back to the same page it is on, which is a common pattern in server-rendered sites. method chooses how the data travels, and there are only two values you will use: get and post.
With method="get", the form's data is appended to the URL as a query string — you have seen this every time you search for something and the address bar fills up with ?q=html+tutorial. Because it is in the URL, the result can be bookmarked, shared and reloaded, and the browser keeps it in history. That is exactly what you want for a search or a filter, where the data describes what to show.
With method="post", the data travels in the body of the request instead of in the address. It does not appear in the URL, is not stored in browser history, has no practical size limit, and is not re-sent by simply reloading the page. Use it whenever the submission changes something — creating an account, placing an order, posting a comment — and whenever the data is private, such as a password.
Two clarifications that matter. First, if you omit method entirely, the browser uses GET; a login form written without a method attribute puts the password in the URL. Second, POST is not encryption. It only means the data is not in the address bar. Anyone watching the connection can read it unless the site is served over HTTPS, which is what actually protects it.
<!-- A search: GET is right. The result is a shareable URL. -->
<form action="/search" method="get">
<label for="q">Search events</label>
<input type="search" id="q" name="q">
<button type="submit">Search</button>
</form>
<!-- submits to: /search?q=robotics -->
<!-- A registration: POST is right. It creates something. -->
<form action="/register" method="post">
<label for="pwd">Choose a password</label>
<input type="password" id="pwd" name="password">
<button type="submit">Create account</button>
</form>
<!-- submits to: /register with the data in the request body -->
<!-- No method attribute: this is a GET, and the password ends up in the URL -->
<form action="/login">
<input type="password" name="password">
</form> - GET — data in the URL; bookmarkable, shareable, reloadable; limited length; for reading and filtering
- POST — data in the request body; not in the URL or history; no practical size limit; for anything that changes state
- Omitting
methodgives you GET, which is rarely what you meant on a form with a password - A file upload must be POST and also needs
enctype="multipart/form-data"on the form - Neither method is secure by itself — HTTPS is what protects data in transit
- GET URLs are recorded in browser history, in server access logs, and often in the referrer sent to the next site. That is three separate places a password would be sitting in plain text, which is why the choice of method is a real decision and not a formality.
name Is What Actually Gets Submitted
This is the single most common reason a beginner's form appears to do nothing. When a form is submitted, the browser builds a list of pairs from the controls inside it, and the key in each pair comes from the control's name attribute. A control with no name is not submitted at all. No error, no warning — its value is simply missing on the other side.
id does not help here. It is a different attribute with a different job: connecting a label to its input, and giving CSS and JavaScript something to target. You will usually give a field both, and they are often the same word, which is exactly why the distinction is so easy to lose. Remember it as: id is for the browser, name is for the server.
It follows that the names you choose become the field names your server-side code reads, so choose them deliberately: lowercase, no spaces, descriptive. If the form posts to something you did not write — a form-handling service, or a backend a teammate built — the names must match what that code expects, character for character.
Two related behaviours are worth knowing early. A checkbox is only included in the submission when it is ticked, so an unticked box sends nothing rather than sending "false". And a group of radio buttons must all share the same name — that shared name is what makes them one group where only one can be chosen at a time.
<!-- This field will never reach the server -->
<input type="text" id="email" placeholder="Your email">
<!-- This one will, as email=... -->
<input type="email" id="email" name="email">
<!-- id connects the label; name carries the value -->
<label for="roll">Roll number</label>
<input type="text" id="roll" name="rollnumber">
<!-- ^^ label points here ^^ server reads this key -->
<!-- Radio buttons: one shared name makes them a single group -->
<input type="radio" id="cse" name="branch" value="cse">
<label for="cse">Computer Science</label>
<input type="radio" id="ece" name="branch" value="ece">
<label for="ece">Electronics</label> - You can watch this happen. Open Developer Tools, go to the Network tab, submit the form, and click the request — the Payload or Form Data panel lists exactly the name-and-value pairs that were sent. A field missing from that list is a field missing its
name.
Labels, and Why a Placeholder Is Not One
Every input needs a <label>, and the label must be properly associated with it. The usual way is to give the input an id and the label a for attribute holding that same id. The two values must match exactly — for="Email" will not find id="email", because ids are case-sensitive.
A correctly associated label does two things you get for free. A screen reader announces the label when the user reaches the field, so they know what to type. And clicking the label focuses the field — which on a phone means the tappable target for a small checkbox becomes the whole line of text next to it instead of a twelve-pixel square. Try it on a well-built form and you will notice how much easier it is.
The alternative that constantly gets used instead is placeholder, the grey hint text inside a field. It is not a label and cannot replace one. It disappears the moment the visitor starts typing, so anyone who is interrupted mid-form has no way to recall what the field was for. Its default colour is deliberately low-contrast and often fails accessibility guidelines. Autofill and password managers rely on labels, not placeholders. Use placeholder for a genuine example of the expected format — and keep the label.
There is a second valid way to associate a label: wrap the input inside the label element. That works without any id at all and is handy for checkboxes. Do not, however, use both the wrapping and a mismatched for — pick one approach per field.
<!-- Correct: for and id match exactly -->
<label for="email">Email address</label>
<input type="email" id="email" name="email">
<!-- Also correct: the input is wrapped by the label -->
<label>
<input type="checkbox" name="newsletter" value="yes">
Send me club announcements
</label>
<!-- Broken: nothing has id="email-address" -->
<label for="email-address">Email</label>
<input type="email" id="email" name="email">
<!-- Wrong: a placeholder used instead of a label -->
<input type="email" name="email" placeholder="Email address">
<!-- Right: label for the name, placeholder for the format -->
<label for="phone">Mobile number</label>
<input type="tel" id="phone" name="phone" placeholder="10 digits, no spaces"> - If a design genuinely has no room for a visible label, do not delete it — hide it visually with CSS while keeping it in the markup, or use
aria-labelon the input. A search box with a magnifying-glass button is the usual case. Removing the label entirely is the one option that is never acceptable.
Buttons, and Why Validation Is Not Security
A form needs a way to submit. <button type="submit"> is the modern choice, because unlike <input type="submit"> it can contain markup — an icon next to the text, for instance. There is a trap here worth knowing: inside a form, a <button> with no type attribute defaults to type="submit". So a button you added to do something else with JavaScript will submit and reload the page when clicked, which is a genuinely confusing bug to chase. Always write type="button" on buttons that are not meant to submit.
HTML also gives you validation attributes. required refuses an empty field. type="email" checks the value looks like an email address. minlength, maxlength, min, max and pattern constrain the value further. The browser blocks submission and shows a message, all without a line of JavaScript.
Now the part that matters more than any of it: this validation is user experience, not security. Every one of those rules lives in a file downloaded to the visitor's computer, where it can be edited. Anyone can open Developer Tools and delete the required attribute, or skip your page entirely and send a request straight to your action URL with a tool like curl. The browser is a convenience for cooperative users, not a gate.
So every rule you write in HTML must be written again on the server, and the server's copy is the one that counts. Client-side validation exists so an honest user finds out about a mistake instantly instead of after a page reload. Server-side validation exists because some requests do not come from your form at all. Treat them as two separate jobs and you will never build the vulnerability that comes from trusting the browser.
<!-- Submits the form -->
<button type="submit">Register</button>
<!-- Does NOT submit — needed for any other button in a form -->
<button type="button" onclick="addAnotherMember()">Add another member</button>
<!-- Clears every field. Rarely a good idea: one misclick loses everything. -->
<button type="reset">Clear form</button>
<!-- Browser-side rules: helpful, and trivially bypassed -->
<input type="email" name="email" required>
<input type="text" name="roll" required pattern="[A-Z]{2}-[0-9]{3}">
<input type="number" name="members" min="1" max="4" required> required— the field cannot be left emptyminlength/maxlength— length limits for textmin/max— value limits for numbers and datespattern— the value must match a regular expressiontype="email","url","number"— format checks the browser performs for you- All of the above improve the experience; none of them protect your server
HTML
<h2>Robotics Club — Registration</h2>
<form action="/register" method="post">
<p>
<label for="fullname">Full name</label><br>
<input type="text" id="fullname" name="fullname" required>
</p>
<p>
<label for="email">Email address</label><br>
<input type="email" id="email" name="email"
placeholder="you@college.edu" required>
</p>
<p>
<label for="roll">Roll number</label><br>
<input type="text" id="roll" name="rollnumber"
placeholder="CS-118" required>
</p>
<p>
<label for="members">Team size</label><br>
<input type="number" id="members" name="teamsize"
min="1" max="4" value="1">
</p>
<p>
<label>
<input type="checkbox" name="newsletter" value="yes">
Send me club announcements
</label>
</p>
<p>
<button type="submit">Register</button>
<button type="button">Save draft</button>
</p>
</form>
<p><small>Try submitting with an empty name, then with "abc" in the email
field, and watch the browser stop you.</small></p> CSS
body {
font-family: system-ui, Arial, sans-serif;
line-height: 1.6;
padding: 24px;
max-width: 480px;
}
label {
font-weight: 600;
font-size: 0.95rem;
}
input[type="text"],
input[type="email"],
input[type="number"] {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 1rem;
}
input:focus {
outline: 2px solid #7a0060;
outline-offset: 2px;
}
button {
padding: 10px 18px;
border: none;
border-radius: 6px;
background: #7a0060;
color: #fff;
font-size: 1rem;
cursor: pointer;
}
button[type="button"] {
background: #eee;
color: #333;
} - Click each label in the example above and watch the matching field take focus. That is the quickest way to check that every
forandidpair really matches — if clicking a label does nothing, the association is broken.
