HTML Input Types
HTML5 defines over twenty input types, each optimized for a specific kind of data. The type attribute on the <input> element determines the control's appearance, behavior, and validation rules. Browsers render native controls — date pickers, color pickers, sliders — without requiring any JavaScript.
Choosing the correct input type improves user experience by showing the appropriate mobile keyboard, enabling browser autofill, and providing built-in validation. This section covers every standard input type with practical examples and guidance on when to use each.
| Type | HTML | Description | Mobile Keyboard |
|---|---|---|---|
| text | <input type="text"> | Single-line text input (default) | Standard |
| <input type="email"> | Email with built-in format validation | @ key, .com shortcut | |
| url | <input type="url"> | URL with built-in format validation | .com, / keys |
| tel | <input type="tel"> | Telephone number (no pattern enforced) | Numeric keypad |
| search | <input type="search"> | Search field with clear button | Search key (go) |
| password | <input type="password"> | Masked text entry | Standard |
| number | <input type="number"> | Numeric with step/range controls | Numeric keypad |
| checkbox | <input type="checkbox"> | Boolean toggle (multiple selections) | — |
| radio | <input type="radio"> | Single-select from group (same name) | — |
| date | <input type="date"> | Date picker (YYYY-MM-DD) | Native date picker |
| datetime-local | <input type="datetime-local"> | Date + time picker (no timezone) | Native date/time picker |
| month | <input type="month"> | Month and year picker | Native month picker |
| week | <input type="week"> | Week and year picker | Native week picker |
| time | <input type="time"> | Time picker (HH:MM or HH:MM:SS) | Native time picker |
| file | <input type="file"> | File picker (accept, multiple, capture) | File browser |
| range | <input type="range"> | Slider control (min, max, step, value) | — |
| color | <input type="color"> | Color picker (hexadecimal value) | Native color picker |
| hidden | <input type="hidden"> | Invisible field for passing data | — |
Live preview showing all the major input types rendered in a grid:
Text-based input types handle string data with varying validation rules and keyboard presentations. The text type is the default for all <input> elements. Specialized types like email, url, and tel trigger tailored mobile keyboards and provide basic format validation.
| Type | Validation | Autocomplete | Use Case |
|---|---|---|---|
| text | None (free-form) | on/off | Names, titles, addresses |
| Checks for @ and domain | Email signup, contact forms | ||
| url | Checks for protocol (http://) | url | Website fields, link submission |
| tel | None (free-form) | tel | Phone numbers (use pattern for format) |
| search | None | off | Search bars with clear button |
| password | None (characters masked) | current-password / new-password | Login, password creation |
| 1 | <!-- Text input with validation attributes --> |
| 2 | <label for="fullname">Full Name</label> |
| 3 | <input |
| 4 | type="text" |
| 5 | id="fullname" |
| 6 | name="fullname" |
| 7 | placeholder="Jane Doe" |
| 8 | minlength="2" |
| 9 | maxlength="100" |
| 10 | required |
| 11 | autocomplete="name" |
| 12 | /> |
| 13 | |
| 14 | <!-- Email with custom pattern --> |
| 15 | <label for="email">Work Email</label> |
| 16 | <input |
| 17 | type="email" |
| 18 | id="email" |
| 19 | name="email" |
| 20 | placeholder="jane@company.com" |
| 21 | pattern="[a-z0-9._%+\-]+@[a-z0-9.\-]+.[a-z]{2,}$" |
| 22 | title="Enter a valid email address" |
| 23 | required |
| 24 | autocomplete="email" |
| 25 | /> |
| 26 | |
| 27 | <!-- Telephone with pattern --> |
| 28 | <label for="phone">Phone Number</label> |
| 29 | <input |
| 30 | type="tel" |
| 31 | id="phone" |
| 32 | name="phone" |
| 33 | placeholder="+1-555-0123" |
| 34 | pattern="[+][0-9]{1,3}[0-9]{3,14}" |
| 35 | title="Format: +1-555-0123" |
| 36 | autocomplete="tel" |
| 37 | /> |
| 38 | |
| 39 | <!-- URL input --> |
| 40 | <label for="website">Website</label> |
| 41 | <input |
| 42 | type="url" |
| 43 | id="website" |
| 44 | name="website" |
| 45 | placeholder="https://example.com" |
| 46 | autocomplete="url" |
| 47 | /> |
| 48 | |
| 49 | <!-- Search input --> |
| 50 | <label for="site-search">Search</label> |
| 51 | <input |
| 52 | type="search" |
| 53 | id="site-search" |
| 54 | name="q" |
| 55 | placeholder="Search documentation..." |
| 56 | aria-label="Search the documentation" |
| 57 | /> |
| 58 | |
| 59 | <!-- Password with requirements --> |
| 60 | <label for="password">Create Password</label> |
| 61 | <input |
| 62 | type="password" |
| 63 | id="password" |
| 64 | name="password" |
| 65 | minlength="8" |
| 66 | maxlength="128" |
| 67 | required |
| 68 | autocomplete="new-password" |
| 69 | /> |
| 70 | <small>Min 8 characters</small> |
info
Checkboxes and radio buttons let users make selections. Checkboxes allow multiple independent choices and are toggled on/off independently. Radio buttons restrict the user to exactly one choice within a group — all radio buttons sharing the same name attribute belong to the same group.
| 1 | <!-- Checkboxes — multiple independent choices --> |
| 2 | <fieldset> |
| 3 | <legend>Subscribe to newsletters (select all that apply)</legend> |
| 4 | |
| 5 | <label> |
| 6 | <input type="checkbox" name="newsletters" value="tech" checked /> |
| 7 | Technology |
| 8 | </label> |
| 9 | <label> |
| 10 | <input type="checkbox" name="newsletters" value="design" /> |
| 11 | Design |
| 12 | </label> |
| 13 | <label> |
| 14 | <input type="checkbox" name="newsletters" value="business" /> |
| 15 | Business |
| 16 | </label> |
| 17 | </fieldset> |
| 18 | |
| 19 | <!-- Radio buttons — single choice from group --> |
| 20 | <fieldset> |
| 21 | <legend>Preferred Contact Method</legend> |
| 22 | |
| 23 | <label> |
| 24 | <input type="radio" name="contact" value="email" checked /> |
| 25 | |
| 26 | </label> |
| 27 | <label> |
| 28 | <input type="radio" name="contact" value="phone" /> |
| 29 | Phone |
| 30 | </label> |
| 31 | <label> |
| 32 | <input type="radio" name="contact" value="mail" /> |
| 33 | Postal Mail |
| 34 | </label> |
| 35 | </fieldset> |
| 36 | |
| 37 | <!-- Inline checkbox with explicit label association --> |
| 38 | <input type="checkbox" id="terms" name="terms" required /> |
| 39 | <label for="terms"> |
| 40 | I agree to the <a href="/terms">Terms of Service</a> and |
| 41 | <a href="/privacy">Privacy Policy</a> |
| 42 | </label> |
| 43 | |
| 44 | <!-- Indeterminate checkbox (JavaScript) --> |
| 45 | <input type="checkbox" id="selectAll" /> |
| 46 | <label for="selectAll">Select All Items</label> |
| 47 | |
| 48 | <script> |
| 49 | const selectAll = document.getElementById('selectAll'); |
| 50 | selectAll.indeterminate = true; |
| 51 | </script> |
best practice
Live preview of checkboxes and radio buttons with custom styling:
HTML5 date and time inputs provide native date pickers, time selectors, and combined controls. Browser support is excellent on modern browsers, with each browser rendering its own platform-native picker. The value format for date inputs is always YYYY-MM-DD, regardless of the user's locale.
| Type | Value Format | Example | Attributes |
|---|---|---|---|
| date | YYYY-MM-DD | 2026-07-07 | min, max, step (days) |
| datetime-local | YYYY-MM-DDTHH:MM | 2026-07-07T14:30 | min, max, step (seconds) |
| month | YYYY-MM | 2026-07 | min, max, step (months) |
| week | YYYY-Www | 2026-W28 | min, max, step (weeks) |
| time | HH:MM or HH:MM:SS | 14:30 | min, max, step (seconds) |
| 1 | <!-- Date picker with min/max constraints --> |
| 2 | <label for="appointment">Appointment Date</label> |
| 3 | <input |
| 4 | type="date" |
| 5 | id="appointment" |
| 6 | name="appointment" |
| 7 | min="2026-07-01" |
| 8 | max="2026-12-31" |
| 9 | value="2026-07-15" |
| 10 | required |
| 11 | /> |
| 12 | |
| 13 | <!-- Date and time (no timezone) --> |
| 14 | <label for="meeting">Meeting Start</label> |
| 15 | <input |
| 16 | type="datetime-local" |
| 17 | id="meeting" |
| 18 | name="meeting" |
| 19 | min="2026-07-07T09:00" |
| 20 | max="2026-07-07T17:00" |
| 21 | step="900" |
| 22 | /> |
| 23 | |
| 24 | <!-- Month picker --> |
| 25 | <label for="billing-month">Billing Month</label> |
| 26 | <input |
| 27 | type="month" |
| 28 | id="billing-month" |
| 29 | name="billing-month" |
| 30 | min="2026-01" |
| 31 | max="2026-12" |
| 32 | /> |
| 33 | |
| 34 | <!-- Week picker --> |
| 35 | <label for="sprint-week">Sprint Week</label> |
| 36 | <input |
| 37 | type="week" |
| 38 | id="sprint-week" |
| 39 | name="sprint-week" |
| 40 | min="2026-W01" |
| 41 | max="2026-W52" |
| 42 | /> |
| 43 | |
| 44 | <!-- Time picker with step --> |
| 45 | <label for="start-time">Start Time</label> |
| 46 | <input |
| 47 | type="time" |
| 48 | id="start-time" |
| 49 | name="start-time" |
| 50 | min="09:00" |
| 51 | max="17:00" |
| 52 | step="1800" |
| 53 | value="09:00" |
| 54 | /> |
| 55 | |
| 56 | <!-- Combining date and time into separate fields --> |
| 57 | <label for="start-date">Departure Date</label> |
| 58 | <input type="date" id="start-date" name="start-date" required /> |
| 59 | |
| 60 | <label for="start-time">Departure Time</label> |
| 61 | <input type="time" id="start-time" name="start-time" required /> |
pro tip
The type="file" input opens the system file picker, letting users select files from their local device. The accept attribute filters allowed file types, multiple enables batch selection, and capture directs mobile browsers to the camera or microphone.
| Attribute | Example | Description |
|---|---|---|
| accept | accept="image/*,.pdf" | Comma-separated MIME types or file extensions |
| multiple | multiple | Allow selecting more than one file |
| capture | capture="environment" | Direct camera/microphone access (mobile) |
| required | required | Form cannot submit without a file |
| 1 | <!-- Single image upload with format filter --> |
| 2 | <label for="avatar">Profile Picture</label> |
| 3 | <input |
| 4 | type="file" |
| 5 | id="avatar" |
| 6 | name="avatar" |
| 7 | accept="image/png,image/jpeg,image/webp" |
| 8 | required |
| 9 | /> |
| 10 | |
| 11 | <!-- Multiple PDF upload --> |
| 12 | <label for="documents">Upload Documents</label> |
| 13 | <input |
| 14 | type="file" |
| 15 | id="documents" |
| 16 | name="documents" |
| 17 | multiple |
| 18 | accept=".pdf,.doc,.docx" |
| 19 | /> |
| 20 | |
| 21 | <!-- Mobile camera capture --> |
| 22 | <label for="photo">Take a Photo</label> |
| 23 | <input |
| 24 | type="file" |
| 25 | id="photo" |
| 26 | name="photo" |
| 27 | accept="image/*" |
| 28 | capture="environment" |
| 29 | /> |
| 30 | |
| 31 | <!-- File preview with File API --> |
| 32 | <input type="file" id="fileInput" accept="image/*" /> |
| 33 | <img id="preview" alt="File preview" style="display:none;max-width:200px;margin-top:8px;" /> |
| 34 | |
| 35 | <script> |
| 36 | const fileInput = document.getElementById('fileInput'); |
| 37 | const preview = document.getElementById('preview'); |
| 38 | |
| 39 | fileInput.addEventListener('change', () => { |
| 40 | const file = fileInput.files[0]; |
| 41 | if (file) { |
| 42 | const reader = new FileReader(); |
| 43 | reader.onload = (e) => { |
| 44 | preview.src = e.target.result; |
| 45 | preview.style.display = 'block'; |
| 46 | }; |
| 47 | reader.readAsDataURL(file); |
| 48 | } |
| 49 | }); |
| 50 | </script> |
warning
The range input renders a slider for selecting a numeric value within a range. The color input opens the system color picker and returns a hexadecimal color value. Both provide native controls without JavaScript.
| 1 | <!-- Range slider with all attributes --> |
| 2 | <label for="volume">Volume: <span id="volumeValue">65</span></label> |
| 3 | <input |
| 4 | type="range" |
| 5 | id="volume" |
| 6 | name="volume" |
| 7 | min="0" |
| 8 | max="100" |
| 9 | step="1" |
| 10 | value="65" |
| 11 | /> |
| 12 | |
| 13 | <script> |
| 14 | const volume = document.getElementById('volume'); |
| 15 | const volumeValue = document.getElementById('volumeValue'); |
| 16 | volume.addEventListener('input', () => { |
| 17 | volumeValue.textContent = volume.value; |
| 18 | }); |
| 19 | </script> |
| 20 | |
| 21 | <!-- Color picker --> |
| 22 | <label for="theme-color">Theme Color</label> |
| 23 | <input |
| 24 | type="color" |
| 25 | id="theme-color" |
| 26 | name="theme-color" |
| 27 | value="#00FF41" |
| 28 | /> |
| 29 | |
| 30 | <!-- Range with custom step and dual value display --> |
| 31 | <label for="price">Price: $<span id="priceValue">50</span></label> |
| 32 | <input |
| 33 | type="range" |
| 34 | id="price" |
| 35 | name="price" |
| 36 | min="10" |
| 37 | max="200" |
| 38 | step="5" |
| 39 | value="50" |
| 40 | /> |
| 41 | <datalist id="price-ticks"> |
| 42 | <option value="10" label="$10"></option> |
| 43 | <option value="50" label="$50"></option> |
| 44 | <option value="100" label="$100"></option> |
| 45 | <option value="150" label="$150"></option> |
| 46 | <option value="200" label="$200"></option> |
| 47 | </datalist> |
| 48 | |
| 49 | <script> |
| 50 | const price = document.getElementById('price'); |
| 51 | const priceValue = document.getElementById('priceValue'); |
| 52 | price.addEventListener('input', () => { |
| 53 | priceValue.textContent = price.value; |
| 54 | }); |
| 55 | </script> |
pro tip
Input attributes control validation constraints, user experience, autofill behavior, and styling. Combining these attributes correctly determines how the input behaves, how it is validated, and how it interacts with browser autofill.
| Attribute | Applies To | Description |
|---|---|---|
| placeholder | text, search, email, url, tel, password, textarea | Hint text shown before input — not a substitute for a label |
| required | Most input types | Field must have a value before form submission |
| readonly | text, textarea, select, date, time | Value cannot be edited but is still submitted |
| disabled | All input types | Not editable, not submitted, grayed out |
| minlength | text, email, url, tel, search, password, textarea | Minimum character count |
| maxlength | text, email, url, tel, search, password, textarea | Maximum character count |
| pattern | text, tel, email, url, search, password | Regex pattern for validation |
| min / max | number, date, range, datetime-local, month, week, time | Numeric or date range constraints |
| step | number, date, range, datetime-local, month, week, time | Increment granularity |
| autocomplete | All input types | Browser autofill behavior (on, off, or specific token) |
| autofocus | Most input types | Automatically focus this input on page load (use once) |
| spellcheck | text, textarea, search | Enable or disable spell checking |
| inputmode | All input types | Hint for the on-screen keyboard type |
| 1 | <!-- Complete attribute example --> |
| 2 | <label for="username">Username</label> |
| 3 | <input |
| 4 | type="text" |
| 5 | id="username" |
| 6 | name="username" |
| 7 | placeholder="johndoe" |
| 8 | value="" |
| 9 | required |
| 10 | minlength="3" |
| 11 | maxlength="20" |
| 12 | pattern="[a-zA-Z0-9_]+" |
| 13 | title="Letters, numbers, and underscores only" |
| 14 | autocomplete="username" |
| 15 | autofocus |
| 16 | spellcheck="false" |
| 17 | /> |
| 18 | |
| 19 | <!-- Readonly vs disabled --> |
| 20 | <input type="text" value="Cannot edit" readonly /> |
| 21 | <input type="text" value="Grayed out" disabled /> |
| 22 | |
| 23 | <!-- Number with min/max/step --> |
| 24 | <label for="quantity">Quantity</label> |
| 25 | <input |
| 26 | type="number" |
| 27 | id="quantity" |
| 28 | name="quantity" |
| 29 | min="1" |
| 30 | max="99" |
| 31 | step="1" |
| 32 | value="1" |
| 33 | required |
| 34 | /> |
| 35 | |
| 36 | <!-- Email with inputmode hint --> |
| 37 | <label for="email">Email</label> |
| 38 | <input |
| 39 | type="email" |
| 40 | id="email" |
| 41 | name="email" |
| 42 | inputmode="email" |
| 43 | autocomplete="email" |
| 44 | required |
| 45 | /> |
| 46 | |
| 47 | <!-- Date with constraints --> |
| 48 | <label for="arrival">Arrival Date</label> |
| 49 | <input |
| 50 | type="date" |
| 51 | id="arrival" |
| 52 | name="arrival" |
| 53 | min="2026-07-01" |
| 54 | max="2026-12-31" |
| 55 | required |
| 56 | /> |
best practice
CSS pseudo-classes enable styling input states without JavaScript. The browser automatically applies classes like :valid, :invalid, :required, :disabled, and :focus. The newer :user-valid and :user-invalid pseudo-classes only apply after the user has interacted with the field.
| 1 | /* Base input styling */ |
| 2 | input, textarea, select { |
| 3 | font-family: inherit; |
| 4 | font-size: 14px; |
| 5 | padding: 10px 12px; |
| 6 | border: 1px solid #d1d5db; |
| 7 | border-radius: 6px; |
| 8 | background: #fff; |
| 9 | color: #1a1a1a; |
| 10 | transition: border-color 0.2s, box-shadow 0.2s; |
| 11 | outline: none; |
| 12 | width: 100%; |
| 13 | } |
| 14 | |
| 15 | /* Focus state */ |
| 16 | input:focus { |
| 17 | border-color: #3b82f6; |
| 18 | box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15); |
| 19 | } |
| 20 | |
| 21 | /* Valid state */ |
| 22 | input:valid { |
| 23 | border-color: #22c55e; |
| 24 | } |
| 25 | |
| 26 | /* Invalid state (before user interaction) */ |
| 27 | input:invalid { |
| 28 | border-color: #d1d5db; |
| 29 | } |
| 30 | |
| 31 | /* User-interacted invalid */ |
| 32 | input:user-invalid { |
| 33 | border-color: #ef4444; |
| 34 | box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15); |
| 35 | } |
| 36 | |
| 37 | /* User-interacted valid */ |
| 38 | input:user-valid { |
| 39 | border-color: #22c55e; |
| 40 | box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.15); |
| 41 | } |
| 42 | |
| 43 | /* Disabled */ |
| 44 | input:disabled { |
| 45 | opacity: 0.5; |
| 46 | cursor: not-allowed; |
| 47 | background: #f9fafb; |
| 48 | } |
| 49 | |
| 50 | /* Readonly */ |
| 51 | input:read-only { |
| 52 | border-style: dashed; |
| 53 | background: #f9fafb; |
| 54 | } |
| 55 | |
| 56 | /* Placeholder styling */ |
| 57 | input::placeholder { |
| 58 | color: #9ca3af; |
| 59 | } |
| 60 | |
| 61 | /* Required indicator */ |
| 62 | input:required + .label-text::after { |
| 63 | content: " *"; |
| 64 | color: #ef4444; |
| 65 | } |
| 66 | |
| 67 | /* Range slider cross-browser */ |
| 68 | input[type="range"] { |
| 69 | -webkit-appearance: none; |
| 70 | appearance: none; |
| 71 | height: 6px; |
| 72 | background: #e5e7eb; |
| 73 | border-radius: 3px; |
| 74 | padding: 0; |
| 75 | } |
| 76 | |
| 77 | input[type="range"]::-webkit-slider-thumb { |
| 78 | -webkit-appearance: none; |
| 79 | appearance: none; |
| 80 | width: 18px; |
| 81 | height: 18px; |
| 82 | border-radius: 50%; |
| 83 | background: #3b82f6; |
| 84 | cursor: pointer; |
| 85 | border: 2px solid #fff; |
| 86 | box-shadow: 0 1px 3px rgba(0,0,0,0.2); |
| 87 | } |
pro tip
Accessible input controls ensure all users — including those using screen readers or keyboard navigation — can successfully interact with forms. The foundation is proper labeling with <label>, descriptive error messages linked via aria-describedby, and invalid state indicators via aria-invalid.
| Technique | Implementation | Purpose |
|---|---|---|
| Explicit label | <label for="id">Text</label> | Associates label with input via matching for/id |
| Implicit label | <label><input>Text</label> | Wraps input inside label element |
| aria-label | aria-label="Search" | Label when no visible text is possible |
| aria-describedby | aria-describedby="hint-id error-id" | Links input to help text or error message |
| aria-invalid | aria-invalid="true" | Indicates the field has a validation error |
| aria-required | aria-required="true" | Announces field as required to screen reader |
| role="alert" | role="alert" on error container | Immediately announces dynamic error content |
| Fieldset/legend | <fieldset><legend> | Groups related inputs with a shared label |
| 1 | <!-- Properly labeled form with error handling --> |
| 2 | <form id="signupForm" novalidate> |
| 3 | <fieldset> |
| 4 | <legend>Account Information</legend> |
| 5 | |
| 6 | <label for="signup-name">Full Name</label> |
| 7 | <input |
| 8 | type="text" |
| 9 | id="signup-name" |
| 10 | name="name" |
| 11 | required |
| 12 | minlength="2" |
| 13 | aria-required="true" |
| 14 | aria-describedby="name-hint name-error" |
| 15 | /> |
| 16 | <span id="name-hint" class="hint"> |
| 17 | Enter your full name (min 2 characters) |
| 18 | </span> |
| 19 | <span id="name-error" class="error" role="alert"></span> |
| 20 | |
| 21 | <label for="signup-email">Email Address</label> |
| 22 | <input |
| 23 | type="email" |
| 24 | id="signup-email" |
| 25 | name="email" |
| 26 | required |
| 27 | aria-required="true" |
| 28 | aria-describedby="email-error" |
| 29 | /> |
| 30 | <span id="email-error" class="error" role="alert"></span> |
| 31 | </fieldset> |
| 32 | |
| 33 | <button type="submit">Create Account</button> |
| 34 | </form> |
| 35 | |
| 36 | <script> |
| 37 | const form = document.getElementById('signupForm'); |
| 38 | form.addEventListener('submit', (e) => { |
| 39 | e.preventDefault(); |
| 40 | let hasError = false; |
| 41 | |
| 42 | const nameInput = document.getElementById('signup-name'); |
| 43 | const nameError = document.getElementById('name-error'); |
| 44 | const emailInput = document.getElementById('signup-email'); |
| 45 | const emailError = document.getElementById('email-error'); |
| 46 | |
| 47 | // Validate name |
| 48 | if (!nameInput.checkValidity()) { |
| 49 | nameError.textContent = 'Name is required (min 2 characters)'; |
| 50 | nameInput.setAttribute('aria-invalid', 'true'); |
| 51 | hasError = true; |
| 52 | } else { |
| 53 | nameError.textContent = ''; |
| 54 | nameInput.removeAttribute('aria-invalid'); |
| 55 | } |
| 56 | |
| 57 | // Validate email |
| 58 | if (!emailInput.checkValidity()) { |
| 59 | emailError.textContent = 'A valid email is required'; |
| 60 | emailInput.setAttribute('aria-invalid', 'true'); |
| 61 | hasError = true; |
| 62 | } else { |
| 63 | emailError.textContent = ''; |
| 64 | emailInput.removeAttribute('aria-invalid'); |
| 65 | } |
| 66 | |
| 67 | if (!hasError) { |
| 68 | alert('Form submitted successfully!'); |
| 69 | } |
| 70 | }); |
| 71 | </script> |
best practice
Live preview of an accessible form with labels, hints, and validation: