HTML Forms & Validation
Forms collect user input — from simple text fields to complex multi-step workflows. They’re essential for login pages, search bars, contact forms, and more.
Basic Form Structure
Every form uses the <form> element:
<form action="/submit" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" />
<button type="submit">Submit</button>
</form>
Key attributes:
action— URL where form data is sentmethod— HTTP method (GET or POST)
Labels
Always pair inputs with labels for accessibility:
<label for="email">Email</label>
<input type="email" id="email" name="email" />
Why labels matter:
- Clicking the label focuses the input
- Screen readers announce the label
- Improves touch targets on mobile
Input Types
HTML5 provides many specialized input types:
<!-- Text -->
<input type="text" name="username" />
<!-- Email (validates format) -->
<input type="email" name="email" />
<!-- Password (hides input) -->
<input type="password" name="password" />
<!-- Number -->
<input type="number" name="age" min="0" max="120" />
<!-- Phone -->
<input type="tel" name="phone" />
<!-- URL -->
<input type="url" name="website" />
<!-- Date -->
<input type="date" name="birthday" />
<!-- Time -->
<input type="time" name="appointment" />
<!-- Color -->
<input type="color" name="favorite-color" />
<!-- File -->
<input type="file" name="avatar" accept="image/*" />
<!-- Range (slider) -->
<input type="range" name="volume" min="0" max="100" />
<!-- Search -->
<input type="search" name="query" />
Textarea
For multi-line text input:
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" cols="40">
Default text here...
</textarea>
Select Dropdown
For choosing from a list of options:
<label for="country">Country</label>
<select id="country" name="country">
<option value="">Select a country</option>
<option value="eg">Egypt</option>
<option value="sa">Saudi Arabia</option>
<option value="ae">UAE</option>
</select>
Checkboxes and Radio Buttons
<!-- Checkbox (multiple selections) -->
<fieldset>
<legend>Interests</legend>
<label>
<input type="checkbox" name="interests" value="html" /> HTML
</label>
<label>
<input type="checkbox" name="interests" value="css" /> CSS
</label>
<label>
<input type="checkbox" name="interests" value="js" /> JavaScript
</label>
</fieldset>
<!-- Radio button (single selection) -->
<fieldset>
<legend>Experience Level</legend>
<label>
<input type="radio" name="level" value="beginner" /> Beginner
</label>
<label>
<input type="radio" name="level" value="intermediate" /> Intermediate
</label>
<label>
<input type="radio" name="level" value="advanced" /> Advanced
</label>
</fieldset>
Validation Attributes
HTML5 provides built-in validation:
<!-- Required field -->
<input type="text" name="name" required />
<!-- Minimum/maximum length -->
<input type="text" name="username" minlength="3" maxlength="20" />
<!-- Pattern matching (regex) -->
<input type="tel" name="phone" pattern="[0-9]{10}" />
<!-- Min/max for numbers -->
<input type="number" name="age" min="18" max="120" />
<!-- Email validation -->
<input type="email" name="email" required />
<!-- URL validation -->
<input type="url" name="website" />
Validation Messages
Custom validation messages with JavaScript:
<form id="myForm">
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<span class="error" aria-live="polite"></span>
<button type="submit">Submit</button>
</form>
<script>
const form = document.getElementById('myForm');
const email = document.getElementById('email');
const error = document.querySelector('.error');
email.addEventListener('input', () => {
if (email.validity.valid) {
error.textContent = '';
} else {
error.textContent = getErrorMessage(email);
}
});
function getErrorMessage(input) {
if (input.validity.valueMissing) return 'Email is required';
if (input.validity.typeMismatch) return 'Please enter a valid email';
return '';
}
</script>
Fieldset and Legend
Group related form elements:
<form>
<fieldset>
<legend>Personal Information</legend>
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</fieldset>
<fieldset>
<legend>Preferences</legend>
<label>
<input type="checkbox" name="newsletter" /> Subscribe to newsletter
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
Complete Example
Here’s a complete registration form:
<form action="/register" method="POST">
<h2>Create Account</h2>
<fieldset>
<legend>Account Details</legend>
<div>
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
required
minlength="3"
maxlength="20"
pattern="[a-zA-Z0-9_]+"
/>
<small>3-20 characters, letters, numbers, and underscores only</small>
</div>
<div>
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
required
minlength="8"
/>
<small>At least 8 characters</small>
</div>
</fieldset>
<fieldset>
<legend>Profile</legend>
<div>
<label for="bio">Bio</label>
<textarea id="bio" name="bio" rows="4" maxlength="500"></textarea>
<small>Max 500 characters</small>
</div>
<div>
<label for="role">Role</label>
<select id="role" name="role" required>
<option value="">Select a role</option>
<option value="student">Student</option>
<option value="developer">Developer</option>
<option value="designer">Designer</option>
</select>
</div>
</fieldset>
<label>
<input type="checkbox" name="terms" required />
I agree to the <a href="/terms">Terms of Service</a>
</label>
<button type="submit">Create Account</button>
</form>
Common Mistakes
- Missing labels — Inputs without labels hurt accessibility
- Using placeholder as label — Placeholder disappears when typing
- No validation — Always validate on both client and server
- Missing required attribute — Let HTML handle basic validation
- Not grouping related fields — Use fieldset for complex forms