Python · SQL · Web Dev · Java · AI/ML tracks launching soon — your one platform for all of IT
Beginner+200 XP

HTML Forms — Inputs & Validation Basics

form, every common input type, labels, placeholder, and the built-in validation attributes browsers already give you for free.

45 min August 2026
// Part 01 — The form Element

Everything a Form Needs to Actually Submit

A <form> element wraps every input that should be submitted together, and its two most important attributes decide where and how that submission happens: action (the URL the data is sent to) and method (usually GET or POST).

The minimum a working form needs
<form action="/submit-signup" method="POST">
  <input type="text" name="email">
  <button type="submit">Sign up</button>
</form>

Without a name attribute on an input, its value is simply never included in the submitted data at all — this is one of the single most common reasons a "working" form silently submits incomplete data, covered in depth in the Common Mistakes section below.

// Part 02 — Input Types, and Why They Are Not Interchangeable

Every Common Input Type, With Real Behavioural Differences

The type attribute on <input> is not cosmetic — each type changes the keyboard shown on mobile, the built-in validation applied, and sometimes the entire UI the browser renders.

The types you will actually use
<input type="text">      <!-- plain single-line text -->
<input type="email">     <!-- validates a basic email SHAPE, shows an email keyboard on mobile -->
<input type="password">  <!-- masks characters as they're typed -->
<input type="number">    <!-- spinner arrows, numeric keyboard, rejects non-numeric text entry -->
<input type="tel">       <!-- numeric-leaning keyboard on mobile, NO format validation at all -->
<input type="url">       <!-- validates a basic URL shape -->
<input type="date">      <!-- a native date picker widget -->
<input type="checkbox">  <!-- boolean toggle -->
<input type="radio">     <!-- one choice from a group (Part 07 of the next module covers grouping) -->
<input type="hidden">    <!-- submitted with the form, never shown or editable by the user -->
⚠️ Important
type="number" is genuinely tricky and often the wrong choice. It rejects leading zeros (so a ZIP code like 02139 becomes 2139), and its spinner arrows are frequently unwanted UI. For anything that looks like a number but is really an identifier — ZIP codes, phone numbers, credit card numbers — type="text" with a pattern attribute (Part 05) is usually the better, more correct choice.
// Part 03 — The label Element

Not Decorative — a Real Programmatic Connection

A <label> is not just text sitting near an input — its for attribute, matched against the input's id, creates a genuine programmatic association that screen readers depend on and that expands the input's clickable area for every user.

A correctly associated label
<label for="email-address">Email address</label>
<input type="email" id="email-address" name="email">

<!-- Clicking the WORD "Email address" now focuses the input — try it in a real browser -->
The implicit alternative — wrapping the input
<label>
  Email address
  <input type="email" name="email">
</label>
<!-- No "for"/"id" pairing needed — the wrapping relationship IS the association -->

Both forms are valid and equally accessible. The explicit for/id form is generally preferred in real codebases because it doesn't constrain the input's position in the DOM relative to its label, making more layout options possible.

placeholder is not a label — a genuinely common, genuinely serious mistake

Wrong — no real label at all
<input type="email" name="email" placeholder="Email address">
<!-- Looks fine visually. Has NO accessible name. A screen reader announces "edit text, blank". -->
⚠️ Important
placeholder text disappears the instant the user starts typing — a sighted user who gets interrupted mid-form and comes back later has already lost the field's label. A screen reader user never had it in the first place; placeholder text is not reliably announced as a label by assistive technology. Always pair every input with a real <label>; use placeholder only for a genuine example of the expected format, never as a replacement for the label itself.
// Part 04 — required and Basic Constraints

The Browser Blocks Submission Before Your Code Ever Runs

required, min, max, minlength, maxlength
<input type="email" name="email" required>
<input type="number" name="age" min="13" max="120">
<input type="password" name="password" minlength="8" maxlength="64" required>

These constraint attributes are checked entirely by the browser, before any form submission is even attempted — if required is present and the field is empty, the browser blocks submission and shows its own native validation message, with zero JavaScript written.

🎯 Pro Tip
Built-in validation is a real first line of defense, but never the only one. It can be bypassed entirely — disabled JavaScript doesn't affect it, but a request crafted directly against your server's endpoint (via curl, a script, or a malicious actor) skips the browser and its validation completely. Server-side validation of every submitted value is still mandatory; client-side/browser validation is a UX improvement, not a security boundary.
// Part 05 — pattern

Custom Validation Rules With Regular Expressions

pattern — a regular expression the value must match
<input type="text" name="zip" pattern="[0-9]{5}" title="A 5-digit ZIP code" required>

<input type="text" name="phone"
       pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
       title="Format: 123-456-7890"
       placeholder="123-456-7890">

The title attribute here is not a tooltip decoration — many browsers surface it directly inside the native validation error message ("Please match the requested format:" + the title text), so it should describe the expected format in plain language, not restate the regex.

// Part 06 — :valid and :invalid

Styling Based on Validation State, With No JavaScript

A brief preview — full CSS pseudo-class coverage comes in the CSS Selectors module
input:invalid {
  border-color: #ff4757;
}

input:valid {
  border-color: #00e676;
}

/* An empty required field is :invalid the instant the page loads — this
   often means every required field shows red before the user has typed
   anything at all, which usually reads as broken rather than helpful */
input:placeholder-shown:invalid {
  border-color: initial;  /* suppress the red state until the user actually interacts */
}

These pseudo-classes update live as the user types, reflecting exactly what the browser's own constraint validation (required/pattern/min/max) currently thinks of the field — no JavaScript event listener required to keep the styling in sync.

// Part 07 — Real World
💼 What This Looks Like at Work

A Signup Form Silently Dropping Fields, at a Denver SaaS Startup

Scenario — SaaS startup, Denver · Signup form bug

A newly redesigned signup form visually looks identical to the old one — same fields, same layout. Support starts getting tickets from users saying their company name never shows up anywhere after signing up, even though they clearly typed it in.

The redesigned markup — visually identical, structurally broken
<label>Company name</label>
<input type="text" id="company" placeholder="Acme Inc.">
<!-- no "name" attribute on the input at all -->

What actually happened

The designer who rebuilt the form's markup copied the visual structure carefully but dropped the name attribute on several inputs during the rewrite — without it, the browser simply never includes that field in the submitted form data at all, no error, no warning, nothing visibly different in the UI. The field renders, accepts input, looks completely normal, and is silently absent from every single submission. The team's own retrospective: "the browser did exactly what we told it — we just told it to submit a form with an unnamed field, which means submit nothing for that field at all."

// Part 08 — Misconceptions

Four Misconceptions About HTML Forms

"placeholder text is a perfectly good substitute for a label"
It disappears the moment the user starts typing, and is not reliably announced as a label by screen readers. Every input needs a real <label> — placeholder is only for a format example.
"Built-in browser validation (required/pattern) means you never need to validate on the server"
Client-side validation is entirely bypassable — a request sent directly to your endpoint (not through the browser form UI at all) skips it completely. Server-side validation of every value remains mandatory regardless of what the browser already checked.
"type='number' is always the right choice for anything numeric-looking"
It strips leading zeros (breaking ZIP codes and similar identifiers), adds spinner arrows that are often unwanted, and rejects formatting characters. For number-shaped identifiers rather than true quantities, type="text" with a pattern attribute is usually more correct.
"An input without a name attribute still gets submitted, just under a generic key"
It is not submitted at all — omitted entirely from the form data, with no error or warning. This is one of the most common real causes of a form that silently drops a field, exactly as shown in the Real World example above.
// Part 09 — Interview Prep

5 Interview Questions — With Complete Answers

Why does an <input> without a name attribute cause data to silently go missing on submit?
The name attribute is what the browser uses as the key when serializing form data for submission — without it, the browser has no key to submit the value under, so the field is omitted from the submitted data entirely, with no error shown anywhere.
What is the difference between the two ways of associating a label with an input?
The explicit form (label for="x" + input id="x") creates the association via matching attribute values and does not require the input to be nested inside the label in the DOM. The implicit form (wrapping the input directly inside the label) uses the DOM nesting itself as the association, with no for/id needed. Both are equally accessible.
Why is client-side (browser) form validation not sufficient on its own?
It can be bypassed entirely by any request that does not go through the browser's own form submission UI — a script or tool hitting the endpoint directly skips all of it. Client-side validation is a UX convenience; server-side validation of every submitted value is the actual security/data-integrity boundary.
What does the pattern attribute do, and what role does the title attribute play alongside it?
pattern supplies a regular expression the input's value must match to be considered valid by the browser's built-in constraint validation. title, in this context, is not a generic tooltip — many browsers insert its text directly into the native validation error message, so it should plainly describe the expected format.
Why might type="number" be the wrong choice for a field like a ZIP code or phone number?
type="number" treats the value as a true numeric quantity — it strips leading zeros (breaking a ZIP code like 02139), typically adds spinner UI that makes no sense for an identifier, and rejects any formatting characters. type="text" combined with a pattern attribute is usually the more correct choice for number-LOOKING identifiers that are not actually meant for arithmetic.
// Common Mistakes

Form Mistakes Beginners Make Constantly

Forgetting the name attribute on an input
The single most common cause of a form that silently submits incomplete data — the field renders and accepts input normally but is never included in what gets submitted.
Using placeholder instead of a real label
Placeholder text vanishes once the user starts typing and is not reliably treated as an accessible name by screen readers — always pair every input with a genuine <label>.
Reaching for type="number" on identifiers rather than true quantities
Leading zeros get silently stripped, and unwanted spinner arrows appear — use type="text" with a pattern attribute for ZIP codes, phone numbers, and similar number-shaped identifiers instead.
Assuming built-in browser validation is a complete security measure
It only runs in the context of an actual browser-rendered form — any direct request to the server-side endpoint bypasses it completely, so server-side validation of every value remains mandatory.
// Error Library

Issues You Will Hit With Forms — And Exactly Why

Please fill out this field. (native browser validation message)
Cause: A field marked required was left empty at the moment the user tried to submit the form — the browser blocked submission before it ever reached the server.
Fix: This is expected, correct behaviour for a required field — no fix needed unless the field should genuinely not be required.
Please match the requested format. (native browser validation message)
Cause: A field with a pattern attribute contains a value that does not match the supplied regular expression.
Fix: Set a clear title attribute describing the expected format in plain language — many browsers show it directly in this error message.
A form submission always reloads the whole page, even though the site otherwise feels app-like
Cause: The default, unmodified behaviour of an HTML form is a full page navigation to the URL in its action attribute — this is normal HTML, not a bug.
Fix: Preventing this requires JavaScript (event.preventDefault() on the submit event) — out of scope for this HTML-focused module, but good to recognise as the expected default rather than a malfunction.

🎯 Key Takeaways

  • The name attribute is what makes an input's value get included in a form submission — omit it and the field is silently dropped, with no error shown anywhere.
  • Input types are not cosmetic — they change the mobile keyboard, the native UI, and the built-in validation applied. type="number" is often the wrong choice for number-shaped identifiers like ZIP codes.
  • A <label> creates a real programmatic association (via for/id or implicit wrapping) that expands the clickable area and is essential for screen readers — placeholder text is never a substitute.
  • required/pattern/min/max are checked entirely by the browser before submission, with zero JavaScript — but they are bypassable via a direct request, so server-side validation remains mandatory.
  • The title attribute on a pattern-constrained input often appears directly inside the browser's native validation error message, so write it as a plain-language format description.
  • :valid/:invalid pseudo-classes let you style based on the browser's live constraint-validation state with no JavaScript event listeners required.

What comes next

Module 09 goes further into forms — select, textarea, fieldset, radio and checkbox groups, and the details of how a form actually submits.

Module 09 → HTML Forms — Advanced
Share

Discussion

0

Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.

Continue with GitHub
Loading...