The type attribute does more than change how an input looks. It tells the browser what kind of data to expect, so it can show the right keyboard on phones, offer pickers for dates and colours, and check the value before the form is submitted.
Input types for text-like data
| Type | Use for | What the browser adds |
|---|---|---|
text | Any single line of text | Nothing special |
email | One email address, or several with multiple | Email keyboard on phones; checks the format |
url | A full URL including the scheme | URL keyboard; checks the format |
tel | Phone numbers | Phone keypad; no format check, because formats vary by country |
search | Search boxes | Often a clear button; search key on phone keyboards |
password | Passwords | Hides the characters; password managers recognise it |
<label for="email">Email address</label>
<input id="email" name="email" type="email">
<label for="website">Website</label>
<input id="website" name="website" type="url" placeholder="https://example.com">
<label for="phone">Phone number</label>
<input id="phone" name="phone" type="tel">
If a browser does not recognise a type, it falls back to text, so trying a newer type is safe.
Numbers and ranges
number is for quantities you count or measure, and it accepts min, max and step:
<label for="qty">Number of loaves</label>
<input id="qty" name="qty" type="number" min="1" max="20" step="1" value="1">
range shows a slider, for values where the exact number matters less than the rough position:
<label for="crust">Crust darkness</label>
<input id="crust" name="crust" type="range" min="1" max="5" value="3">
Do not use type="number" for things that are not really numbers: phone numbers, card numbers, postcodes or account numbers. They can start with zero, be very long, or contain spaces, and number inputs may strip or reject those characters. Use type="text" with inputmode="numeric" instead, as lesson 19 explains.
Dates, times and colours
<label for="class-date">Class date</label>
<input id="class-date" name="class-date" type="date" min="2026-11-01" max="2026-12-20">
<label for="pickup">Pick-up time</label>
<input id="pickup" name="pickup" type="time" min="07:00" max="16:00" step="900">
<label for="icing">Icing colour</label>
<input id="icing" name="icing" type="color" value="#f4c2c2">
Browsers show their own pickers, which already support the keyboard and the user's locale. Whatever the display format, the submitted value is always in ISO format: 2026-11-07 for dates and 09:30 for times. step="900" on a time input means 15-minute steps, because the step is in seconds.
Other types in the same family are datetime-local, month and week. Support for month and week has historically been patchy, so check before relying on them.
Built-in validation
Add constraints with attributes, and the browser blocks submission until the values meet them:
| Attribute | Works with | Rule |
|---|---|---|
required | Most controls | Must not be empty |
minlength, maxlength | Text-like types, textarea | Number of characters |
min, max | number, range, dates and times | Lowest and highest value |
step | number, range, dates and times | Allowed increments |
pattern | Text-like types | Must match a regular expression |
When someone submits an invalid form, the browser focuses the first invalid field and shows a message, such as "Please fill in this field." The message wording and appearance come from the browser and its language settings.
<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Book a class | Riverside Bakery</title>
<style>
input:user-invalid { border: 2px solid #b00020; }
</style>
</head>
<body>
<h1>Book a class</h1>
<form action="/book" method="post">
<p>
<label for="name">Full name</label>
<input id="name" name="name" type="text" required minlength="2" maxlength="80">
</p>
<p>
<label for="email">Email address</label>
<input id="email" name="email" type="email" required>
</p>
<p>
<label for="places">Places (1 to 4)</label>
<input id="places" name="places" type="number" required min="1" max="4" value="1">
</p>
<p>
<label for="postcode">Postcode</label>
<input id="postcode" name="postcode" type="text" required
pattern="[A-Za-z]{1,2}[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}"
aria-describedby="postcode-hint">
<span id="postcode-hint">For example, BS1 4AB</span>
</p>
<button type="submit">Book</button>
</form>
</body>
</html>
How pattern works
pattern takes a JavaScript regular expression. It must match the whole value, so you do not need ^ and $. An empty, optional field is not checked against the pattern; add required if it must be filled.
Modern browsers compile pattern with the regular expression v flag, which is stricter about some characters inside character classes. If a pattern that works elsewhere seems to be ignored, check the Console for a pattern error and escape characters such as - inside square brackets.
Always explain the expected format in visible text, as the postcode hint does. A pattern with no explanation leaves people guessing why their input is rejected. aria-describedby connects the hint to the field so screen readers read it too.
Styling valid and invalid fields
CSS can target the validation state:
:invalidand:validmatch as soon as the page loads. A required, empty field is invalid immediately, so styling:invalidpaints the whole form red before anyone has typed.:user-invalidand:user-validmatch only after the person has interacted with the field or tried to submit. That is almost always what you want.
:user-invalid is supported in all current major browsers. Check Baseline on web.dev if you need to support older versions. Do not rely on colour alone to show an error; the browser's message or your own text should say what is wrong.
Turning validation off
novalidate on the form turns off the browser's checks and messages while keeping the constraint attributes:
<form action="/book" method="post" novalidate>
Teams use this when they show their own error messages with JavaScript, using the same constraints through the Constraint Validation API (checkValidity(), validity and setCustomValidity()). Accessible custom error handling is covered in the Web Accessibility Essentials course.
Browser validation is a convenience, not security. Anyone can remove the attributes in DevTools or send a request directly. Validate every value again on the server.
Summary
- Choose
typeby the data:email,url,tel,number,date,timeand others. - Use
numberonly for real quantities, never for phone or card numbers. - Add
required,minlength,maxlength,min,max,stepandpatternfor built-in checks. - Explain expected formats in visible text, and style errors with
:user-invalid. - Always validate again on the server.