Use input type="number" for real quantities such as an age, a price or a count. min and max set the allowed range, step sets the increment (1 by default, or any for free decimals), and valueAsNumber gives you a number or NaN when the field is empty. For card numbers and postcodes use type="text" with inputmode="numeric" instead.
| Attribute | Example | Effect |
|---|---|---|
| min | min="0" | Lower values fail validation (rangeUnderflow) |
| max | max="10" | Higher values fail validation (rangeOverflow) |
| step | step="0.01" or step="any" | Default 1; values off the step fail (stepMismatch) |
| required | required | An empty field fails (valueMissing) |
| list | list="sizes" | Suggests values from a datalist |
| placeholder | placeholder="e.g. 2" | Hint shown while empty, never a label |
Updated
Number fields look simple, but the rules about steps, empty values and the spinner trip people up. Set the attributes in the tester, type anything into the field, and see the raw value, the parsed number and each validity flag update as you type.
<label for="qty">Quantity</label>
<input type="number" id="qty" name="qty" min="0" max="10" step="1">| Data | Field | Why |
|---|---|---|
| Quantity, age, guests | type="number" min max | A real amount; the spinner and range checks help |
| Price | type="number" step="0.01" | Cents need a decimal step |
| Card number | type="text" inputmode="numeric" | An identifier, not an amount |
| Postal or ZIP code | type="text" inputmode="numeric" | Leading zeros and letters in some countries |
| One-time code | type="text" inputmode="numeric" autocomplete="one-time-code" | Digits only, no spinner |
| Phone number | type="tel" | Has its own input type |
Type 4.999 or a negative number and the outline turns red, because the value breaks the step or the minimum.
<style>
input { font: inherit; padding: 6px 8px; border: 2px solid #94a3b8; border-radius: 8px }
input:invalid { border-color: #dc2626 }
</style>
<label for="price">Price (USD)</label><br>
<input type="number" id="price" min="0" step="0.01" value="19.99">appearance: textfield removes the arrows; the WebKit pseudo-elements cover browsers that still draw them. Arrow keys keep working.
<style>
.plain { appearance: textfield; -moz-appearance: textfield }
.plain::-webkit-inner-spin-button,
.plain::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0 }
</style>
<label for="n">Guests</label><br>
<input class="plain" type="number" id="n" min="1" max="12" value="2">inputmode hints at a numeric keyboard, the pattern checks for five digits, and leading zeros stay intact.
<label for="zip">ZIP code</label><br>
<input type="text" id="zip" inputmode="numeric" pattern="[0-9]{5}" autocomplete="postal-code">valueAsNumber avoids string parsing, and the validity check catches out-of-range and off-step values.
const input = document.querySelector("#qty");
input.addEventListener("input", () => {
const n = input.valueAsNumber;
if (Number.isNaN(n)) return; // empty or not a number
if (!input.checkValidity()) {
console.log(input.validationMessage); // browser's own message
return;
}
console.log("Valid quantity", n);
});
The browser checks the field against min, max and step, and sets a flag on the validity object for each rule it breaks. The form will not submit while a flag is set, and :invalid lets you style the field.
When the field holds text the browser cannot turn into a number, validity.badInput becomes true. Check that flag, or valueAsNumber for NaN, before trusting what you read; the tester shows exactly what your browser reports.
A number input is exposed to assistive technology as a spinbutton. If stepping up and down is not useful for the value, a text field with inputmode="numeric" avoids accidental changes from the scroll wheel or arrow keys.
Ask whether the value is an amount you could add to or subtract from. If it is, use a number field; if it is a code that happens to contain digits, use text.
The default step is 1, so only whole numbers are valid unless the step base is not an integer. Add step="0.5", step="0.01" or step="any" to accept decimals.
NaN. The value property returns an empty string in the same case. You can also set valueAsNumber to write a number back into the field.
Set appearance: textfield on the input. The up and down arrow keys still change the value, because appearance only changes the look. WebKit and Blink also draw spin buttons through pseudo-elements, which the example below hides as well.
Use type="number" when the value is a real quantity and a spinner helps. Use type="text" with inputmode="numeric" and a pattern for digit strings such as card numbers or postal codes, which are identifiers rather than amounts.
Yes, unless it has the required attribute. A number input counts as valid when it is empty or holds a single valid number.
Related: HTML and CSS cheat sheet, Regex cheat sheet, HTML accessibility checker, JavaScript event listeners, HTML validator.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.