Puneet Sharma - Frontend Developer & UI Engineer
Puneet Sharma
Frontend Dev & UI Engineer · 16+ yrs · pixel-perfect HTML, React & WordPress

HTML <input> Types You Should Actually Use in Modern Forms

14 HTML input types explained with live demos: email, tel, date, range, color, file, password toggle, OTP fields and more — fork and edit each one.
HTML input Types You Should Actually Use in Modern Forms

Most forms on the web still use type="text" for everything — email, phone, dates, even six-digit codes — and then rebuild, in JavaScript, behavior the browser already gives away for free. HTML has shipped more than a dozen specialized input types for years: the right one swaps in the correct on-screen keyboard on mobile, opens a native picker instead of a custom widget, and checks its own format before your code ever runs. Reaching for the specific type instead of the generic one is one of the highest-leverage, lowest-effort upgrades a form can get.

The demos below are all real, interactive fields — type into them, drag them, open their pickers. Every embed also has a Fork & Edit button that opens the exact snippet, already running, in My Code — FWD Tools' free in-browser editor. Swap a type, change a min, and watch what the browser does differently — that's a faster way to build real intuition than any explanation.

If input types and form structure in general are still shaky ground, the HTML Playground is worth having open alongside this post — a free, click-based, in-browser course that runs beginner to pro across 42 lessons in 13 chapters, with a dedicated Forms chapter covering exactly this ground as a guided curriculum.

Why the specific type is worth the extra five characters

Changing type="text" to type="email" costs nothing — it's the same number of attributes, often fewer. What it buys back, on every device and in every browser, without a line of script: the correct mobile keyboard layout, built-in format checking on submit, and in several cases (dates, times, colors, files) an entire native picker UI that would otherwise be a whole component to design, build, test, and maintain. Every demo in this post is doing something a plain type="text" field either can't do at all, or can only do by re-implementing what the browser already ships.

1. type="email"

On a phone this opens a keyboard with @ and . on the main layer. Type an address without an @ and submit — the browser blocks it.

The rule:

<input type="email" required>
<input type="email" multiple>

Beyond the mobile keyboard and the built-in "is this shaped like an address" check, few developers know about the multiple attribute: add it, and a single email field accepts a comma-separated list, validating each address in it independently — useful for a "notify these people" field without building a tag-input component.

Best for: any email address field, full stop. Tip: type="email" checks shape, not existence — it happily accepts a@b.co even though that domain may not exist. Confirming a real, reachable inbox still needs a verification email sent server-side.

2. type="tel"

On a phone this opens the numeric phone keypad, not the full alphabet keyboard.

The rule:

<input type="tel" pattern="[0-9\-\+\s\(\)]{7,}">

tel is the one common input type with zero built-in format checking — deliberately, since valid phone number shapes vary enormously by country (spaces, dashes, parentheses, leading + codes all differ). The keyboard swap alone is worth using it; add your own pattern on top if the form needs actual format enforcement.

Best for: any phone number field. Tip: don't reach for type="number" here even though phone numbers are digits — number fields strip a leading 0 and reject the +, dashes, and spaces real phone numbers actually contain.

3. type="url"

Type "example" with no protocol and submit — the browser blocks it. Type "https://example.com" and it passes.

The rule:

<input type="url" placeholder="https://…">

A URL needs a scheme (https://, typically) to be considered valid — a bare domain-looking string like example.com fails the built-in check even though a human would read it as a website. That's worth surfacing to the visitor via the placeholder, since it's the single most common reason this field rejects an otherwise-reasonable-looking answer.

Best for: portfolio links, company websites, any single-URL field. Tip: on mobile this also swaps in a keyboard with / and .com shortcuts, which type="text" never does.

4. type="number"

Use the spinner arrows or type directly — the value can't go below 1 or above 10.

The rule:

<input type="number" min="1" max="10" step="1">

min, max, and step constrain the spinner and what the browser will submit, and :in-range/:out-of-range in CSS can style either state with no script. But this type has a real downside worth knowing before reaching for it by default: it silently drops a leading zero (007 becomes 7 the instant you tab away), rejects a plain +, and in some browsers accepts scientific notation like 1e3 as a valid number. None of that is a bug — it's the type doing exactly what "this is a number" implies.

Best for: genuine arithmetic quantities — cart quantity, age, a rating out of 10. Tip: if the value is never actually going to be added, subtracted, or compared numerically — a phone number, a zip code, a six-digit code — it isn't really a number, and demo #14 below shows the better alternative.

5. type="range"

Drag the slider — it snaps to the tick marks.

The rule:

<input type="range" min="0" max="100" step="25" list="ticks">
<datalist id="ticks">
  <option value="0"></option>
  <option value="25"></option>
  <option value="50"></option>
</datalist>

Most developers who use range never discover it can pair with a <datalist> the same way a text input's autocomplete list does — the browser renders literal tick marks at each listed value and the thumb snaps toward them. The current numeric value itself isn't shown anywhere by default; this demo's live readout above the slider is a few lines of script reading input.value on the input event, which is the standard pattern for surfacing it.

Best for: volume, brightness, a price-range filter — any single value where the relative position matters more than typing an exact number. Tip: a range slider alone gives no visible number; always pair it with a live readout, or visitors are dragging blind.

6. type="search"

Type something, then look at the right edge of the field.

The rule:

<input type="search" placeholder="Search…">

Most browsers render a built-in "clear" (×) button inside a search field the instant it has text — a plain type="text" field never gets this, and reimplementing it means an absolutely positioned button, a click handler, and manual focus management. type="search" also communicates the field's purpose to assistive technology directly, which a generic text input paired with a magnifying-glass icon alone does not.

Best for: any search box — site search, in-page filtering, a command palette's input. Tip: exact clear-button styling varies by browser and can't be customized with CSS; if pixel-perfect control over that × matters more than the free functionality, build your own clear button and accept the type purely for its semantics and keyboard behavior.

7. type="date"

Click the field to open the browser's own native date picker.

The rule:

<input type="date" min="2024-01-01" max="2030-12-31">

The picker's on-screen appearance follows the visitor's OS and locale settings, but the underlying value is always a locale-independent YYYY-MM-DD string — parse it with new Date(value + 'T00:00:00') rather than passing the raw string to Date, since some engines interpret a bare date-only ISO string as UTC midnight and shift it a day when displayed in a behind-UTC timezone.

Best for: birthdates, booking dates, deadlines — any single calendar date. Tip: min and max constrain the picker itself, not just submission — a booking form can make dates in the past simply unselectable rather than selectable-then-rejected.

8. type="time"

A dedicated hour/minute picker — no date attached.

The rule:

<input type="time">

The underlying value is always a 24-hour HH:MM string, regardless of whether the picker itself displays a 12-hour or 24-hour interface — that display choice follows the visitor's OS locale automatically, with zero configuration on your part.

Best for: reminder times, opening hours, appointment slots — any time-of-day value that isn't attached to a specific date. Tip: add a step attribute (in seconds) if the picker needs finer or coarser granularity than the default one-minute increments — step="1" reveals a seconds column, step="900" snaps to 15-minute intervals.

9. type="datetime-local"

Date and time in one picker, one field, one value.

The rule:

<input type="datetime-local">

This replaces what used to require two separate inputs (a date field and a time field) plus script to combine their values into one timestamp. The name's "local" half matters: the value has no timezone attached at all — it's the visitor's own wall-clock date and time, as typed, with no offset. If the value needs to be stored or compared against other timezones server-side, that attachment has to happen explicitly, since the browser never supplies it.

Best for: event start times, appointment scheduling — anywhere a date and a time are really one combined value rather than two independent ones. Tip: don't assume the value is UTC or includes an offset; it's exactly what the visitor's local picker showed, nothing more.

10. type="month" & type="week"

Two narrower siblings of date — pick just a month with no day, or just a week number.

The rule:

<input type="month">   <!-- value like 2026-03 -->
<input type="week">    <!-- value like 2026-W12 -->

Both are genuinely awkward to build correctly by hand — a month picker needs to avoid ever implying a specific day, and a week picker needs to implement ISO week numbering (which doesn't align with calendar months or even always start the same weekday depending on locale). Reaching for either type means the browser has already solved that, rather than a bespoke dropdown-of-months or a week-number lookup table living in your own code.

Best for: month for billing periods, "born in" fields, or subscription cycles; week for weekly reports, sprint planning, or anything that genuinely operates on calendar weeks rather than specific days. Tip: browser support for these two is the least universal of the date family — verify current coverage before depending on either for essential functionality in an older-browser context.

11. type="color"

Click the swatch to open the browser's own color picker.

The rule:

<input type="color" value="#6366f1">

The value is always a lowercase 6-digit hex string (#rrggbb) — no alpha channel, no rgb() function, no color names. That's a genuine limitation if a design system needs transparency or an HSL value picked directly, but for a plain solid color it's an entire picker UI — swatch grid, hue slider, hex input — with zero component code.

Best for: theme customizers, brand color pickers, any single opaque color a visitor selects directly. Tip: if the design needs alpha transparency or an HSL/HSB workflow, this native picker can't do it — that's when a JS color-picker library actually earns its weight.

12. type="password"

Click the eye icon to toggle the field between masked and plain text.

The rule:

<input type="password" autocomplete="new-password">
<input type="password" autocomplete="current-password">

There's no built-in "reveal password" mode — the toggle button in this demo works by swapping the input's type attribute between password and text in script, which is the standard pattern everywhere this UI appears. The autocomplete value matters more than most developers realize: new-password tells the browser this is a signup or change-password field (don't offer a saved login), while current-password tells it this is a login field (do offer one) — getting this backwards is a common source of password managers behaving strangely on a form.

Best for: every password field, always. Tip: pair a signup password field with minlength and your own strength feedback — type="password" masks input, it doesn't evaluate strength on its own.

13. type="file"

Pick one or more images — nothing uploads anywhere; the names just get listed below.

The rule:

<input type="file" accept="image/*" multiple>

accept filters what the native file picker shows — a MIME type like image/*, a specific type like application/pdf, or a file extension like .csv, comma-separated for more than one. multiple is what allows picking more than a single file at once; without it, selecting a second file in the OS picker just replaces the first. The selected files are available in script as a real FileList (input.files), each with a name, size, and type — as this demo's live list shows — before anything is ever sent anywhere.

Best for: avatar uploads, document attachments, CSV imports — anything reading a file from the visitor's own device. Tip: accept is a UI hint, not a security boundary — a visitor can still bypass the picker's filter and choose "all files," so any real type or size validation has to happen again once the file actually arrives at your server.

14. type="text" + inputmode="numeric"

On a phone this still opens a numeric keypad — the same one type="number" gives you — but without a spinner, without scientific notation, and while still storing the value as a plain string.

The rule:

<input
  type="text"
  inputmode="numeric"
  pattern="[0-9]{6}"
  autocomplete="one-time-code">

This is the fix for demo #4's caveat: a six-digit verification code, a PIN, or a ZIP code is a string of digits, not a number you'd ever add or compare — a leading zero matters, and there's no spinner or arithmetic behavior to gain from type="number". inputmode="numeric" is purely a hint to the on-screen keyboard; it changes nothing about how the value validates or submits, so it's paired here with pattern to actually enforce the digits-only, exact-length shape. autocomplete="one-time-code" is a smaller but genuinely useful addition on top: on supporting browsers and OSes it lets the field auto-fill directly from an incoming SMS code.

Best for: OTP/verification codes, PINs, ZIP or postal codes, credit card numbers — any fixed-format digit string that is never used arithmetically. Tip: inputmode has several other values worth knowing beyond numericdecimal (adds a decimal point key), tel, email, url, and search all exist for tuning the keyboard on a plain type="text" field when none of the dedicated input types quite fit.

Common pitfalls

  • Reaching for type="number" for anything that isn't really arithmetic. Phone numbers, ZIP codes, credit card numbers, and OTP codes are all strings of digits, not numbers — and number silently mangles a couple of them (stripping a leading zero, in particular). Demo #14's inputmode="numeric" pattern is almost always the better fit.
  • Forgetting that type="tel" validates nothing on its own. Unlike email or url, there's no built-in format check — the keyboard swap happens regardless, but an actual format constraint needs a pattern added explicitly, as in demo #2.
  • Getting autocomplete="new-password" vs "current-password" backwards. This one swap is what tells a password manager whether to suggest a saved credential (login forms) or generate/save a fresh one (signup forms) — mixing them up is a common, subtle source of odd autofill behavior.
  • Treating accept on a file input as real validation. It filters what the OS picker shows, but a visitor can select "all files" and bypass it entirely — genuine type and size checks still belong on the server, same as every other client-side constraint.
  • Parsing a type="date" value with a bare new Date(value) call. Some JS engines treat a date-only ISO string as UTC midnight, which can display as the previous day in a timezone behind UTC. Append a time (new Date(value + 'T00:00:00')) to parse it in the visitor's local timezone instead.

Frequently asked questions

Do all these input types work the same in every browser?

The core value format for each type is standardized and consistent everywhere, but the picker UI itself is native to the browser and OS — a type="date" field looks different in Chrome on Android than in Safari on iOS, and month/week in particular have the least universal support of the group. Check caniuse.com for the specific type before depending on it for essential functionality in an older-browser context.

What's the actual difference between type="number" and inputmode="numeric"?

type="number" tells the browser the value is a number — it gets a spinner, arithmetic-aware validation, and can silently reformat what you typed (stripping a leading zero, for instance). inputmode="numeric" on a plain type="text" field only changes which on-screen keyboard appears on a touch device; the value stays an untouched string underneath. Use number for values you'll actually do math on, and inputmode="numeric" for digit strings you won't, as demo #14 explains.

Why does my type="tel" field accept literally anything?

Because tel has no built-in format validation at all — that's deliberate, since valid phone formats vary too much across countries for the spec to standardize one. It exists purely to trigger the numeric phone keyboard on mobile. Add a pattern attribute, as demo #2 does, if the field also needs to enforce a specific shape.

Should I still use type="text" for anything?

Yes — names, addresses, free-form comments, and anything else without a more specific native type are exactly what type="text" (the implicit default) is for. The point of this post isn't "avoid text," it's "don't use text when a more specific type already does the job for free."

Can I style these native pickers to match my design?

Partially, and it varies a lot by type and browser. The field itself (border, background, font) styles like any input. The picker popup — the calendar grid for date, the color swatch grid for color — is native browser UI with limited or no CSS access, which is the real tradeoff for getting it for free. If pixel-perfect control over the picker's appearance matters more than that, a JS-built custom picker component is the alternative, at the cost of building and maintaining it yourself.

Does using the right input type help with accessibility too?

Yes, meaningfully. Each specific type communicates its purpose to assistive technology — a screen reader announces a search field differently from a generic text field, and a date field's native picker comes with keyboard navigation and ARIA semantics already built in. A custom-built equivalent has to reimplement all of that by hand to reach the same baseline.

Every demo above ships its full HTML, CSS and JS in the HTML / CSS / JS tabs in its own top bar. The fastest way to actually internalize the differences is to click Fork & Edit on demo #4, change type="number" to type="text" inputmode="numeric", type a leading zero, and watch it stop disappearing. Once you've got a version you like, save it to My Code and it's yours to keep, tweak further, or reuse in a real project.

Recommended next step: open the HTML Playground and go to the Forms chapter — it covers input types, form structure, fieldset and legend, and native form validation as a dedicated, click-based lesson set, built for exactly the pattern this post covers. For more tutorials like this one, browse the HTML label on the blog.

About the author

Puneet Sharma
Puneet Sharma is a freelance web developer and the creator of FWD Tools and WebDevPuneet. Follow him on X/Twitter

Post a Comment