HTML Snippet
HTML Forms
Every input type, every validation attribute, and the accessibility rules that matter.
Last updated: October 2026
Quick answer: Wrap inputs in a <form> with action and method. Use <label for="id"> paired with <input id="id"> for accessibility. Pick the correct type (email, tel, number, date) for free mobile keyboards and validation. Add required, minlength, and pattern for native validation — no JavaScript needed.
🎬 Live Demo
A working form with native validation. Try submitting with empty fields, invalid emails, or short passwords — the browser enforces the rules.
💡 All validation above is native HTML. No JavaScript was written for the rules.
The Basic Form Structure
<form action="/submit" method="POST">
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required>
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
<button type="submit">Submit</button>
</form>
Every form needs three things:
action— the URL the form submits to.method—GETfor search/filters,POSTfor creating/updating data.nameon every input — this is the key sent to the server.
Every Input Type You Need
| Type | Use for | Free benefits |
|---|---|---|
text |
Names, titles, generic text | Basic text keyboard |
email |
Email addresses | @ keyboard + validation |
password |
Passwords | Masked input |
number |
Quantities, ages | Numeric keyboard + stepper |
tel |
Phone numbers | Dial pad keyboard |
url |
Website addresses | .com keyboard + validation |
date |
Birthdays, bookings | Native date picker |
time |
Schedules | Native time picker |
checkbox |
Multi-select options | Native checkbox |
radio |
Single-choice groups | Native radio buttons |
file |
Uploads | Native file picker |
search |
Search inputs | Clear button built-in |
💡 Always use the most specific type. type="email" gives you validation and mobile keyboard automatically. Using type="text" for email means reinventing both.
Native Validation Attributes
All of these work without a single line of JavaScript:
<!-- Required field -->
<input type="text" required>
<!-- Length constraints -->
<input type="text" minlength="3" maxlength="20">
<!-- Numeric range -->
<input type="number" min="18" max="120">
<!-- Regex pattern -->
<input type="text" pattern="[A-Za-z]{3,}">
<!-- Custom error message -->
<input type="text" required
title="Please enter your full name">
<!-- Only shown when the field is valid -->
<input type="email">
What each does:
required— the field cannot be empty.minlength/maxlength— string length limits.min/max— numeric or date limits.pattern— regex check on the input value.title— custom error message shown by the browser.
Accessibility — Labels Are Mandatory
Every input must have a label. There are two ways to associate them:
<!-- Method 1: for + id (preferred) -->
<label for="email">Email address</label>
<input type="email" id="email" name="email">
<!-- Method 2: Wrap the input in the label -->
<label>
Email address
<input type="email" name="email">
</label>
<!-- ❌ Wrong — no label, placeholder isn't a label -->
<input type="email" placeholder="Email address">
Placeholders are not labels. Screen readers announce placeholders as content, but they don't identify the field the way a label does. Also, placeholders disappear when the user types — making the field confusing.
Group related inputs with fieldset:
<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street</label>
<input type="text" id="street" name="street">
<label for="city">City</label>
<input type="text" id="city" name="city">
</fieldset>
Styling Valid, Invalid, and Focus States
CSS pseudo-classes let you style validation state without JavaScript:
/* Default state */
input {
border: 1px solid #475569;
padding: 8px 12px;
border-radius: 6px;
}
/* Focused */
input:focus {
border-color: #3b82f6;
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
/* Invalid — but only after the user has typed */
input:invalid:not(:placeholder-shown) {
border-color: #ef4444;
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}
/* Valid — but only after typing */
input:valid:not(:placeholder-shown) {
border-color: #10b981;
}
/* Required but empty */
input:required:placeholder-shown {
border-color: #475569;
}
💡 Why :not(:placeholder-shown)? Without it, empty required fields get the red border on page load — before the user has done anything. That looks broken. This pseudo-class only applies styles once the user has typed something.
A Complete Production Form
<form action="/signup" method="POST" novalidate>
<fieldset>
<legend>Create your account</legend>
<div class="field">
<label for="name">Full Name</label>
<input type="text" id="name" name="name"
required minlength="2" maxlength="60"
autocomplete="name">
</div>
<div class="field">
<label for="email">Email</label>
<input type="email" id="email" name="email"
required autocomplete="email">
</div>
<div class="field">
<label for="password">Password</label>
<input type="password" id="password" name="password"
required minlength="8"
autocomplete="new-password"
aria-describedby="password-help">
<small id="password-help">
At least 8 characters
</small>
</div>
<div class="field">
<input type="checkbox" id="terms" name="terms" required>
<label for="terms">I agree to the terms</label>
</div>
<button type="submit">Create Account</button>
</fieldset>
</form>
Key details:
autocomplete— helps browsers and password managers fill fields correctly.aria-describedby— connects help text to the input for screen readers.novalidate— disables browser default UI so you can style validation yourself.fieldset + legend— groups related fields with a heading.
🎯 Input Types & Validation Cheatsheet
| Attribute | Applies to | Example |
|---|---|---|
required |
All inputs, textarea, select | <input required> |
minlength |
Text inputs, textarea | minlength="8" |
maxlength |
Text inputs, textarea | maxlength="100" |
min |
number, date, time, range | min="18" |
max |
number, date, time, range | max="120" |
pattern |
Text, tel, email, url | pattern="[0-9]{3}-[0-9]{4}" |
step |
number, range | step="0.01" |
autocomplete |
All inputs | autocomplete="email" |
⚠️ Common Mistakes
- Using a placeholder instead of a label. Placeholders are not accessible. Always add a
<label>. - Using
type="text"for email, phone, or numbers. You lose validation, mobile keyboards, and autofill. - Forgetting the
nameattribute. Inputs withoutnameare not sent to the server on submit. - Using GET for sensitive data. Passwords and tokens appear in the URL. Use POST.
- Not setting
autocomplete. Users expect browsers to fill name, email, and address. Adding the right autocomplete values speeds up forms dramatically. - Relying only on client-side validation. Always validate on the server too. Client validation is UX, not security.
- Disabling browser validation without replacing it. If you add
novalidateto a form, you must implement your own validation. - Not testing keyboard navigation. Tab through the form. Every field should be reachable and the visual focus should be clear.
❓ Frequently Asked Questions
How do I create an HTML form?
Use the <form> element with action (the URL to submit to) and method (GET or POST). Inside, add <label> and <input> pairs for each field. Add a submit button with type=submit.
What is native form validation?
Native form validation uses HTML attributes like required, minlength, maxlength, pattern, min, and max to enforce rules in the browser. The browser shows error messages and prevents submission until the form is valid. No JavaScript required.
What input types should I use?
Use email for email addresses, tel for phone numbers, url for URLs, number for numeric input, date for dates, and password for sensitive input. Each type triggers the correct keyboard on mobile and provides native validation.
How do I make forms accessible?
Always associate every input with a <label> using the for attribute. Group related inputs with <fieldset> and <legend>. Use aria-describedby for help text. Test with a screen reader and keyboard navigation.
Should I use GET or POST for form submission?
Use GET for search forms and filters where the data appears in the URL. Use POST for anything that creates, updates, or deletes data, or for sensitive information like passwords and payment details.