HTML Number Input: min, max, step, Decimals, and valueAsNumber

HTML input type="number" accepts quantities that can be compared or incremented. min and max set the range, and step sets valid intervals. Allowing decimals requires considering both the interval and its base; numeric-looking phone numbers or postal codes generally need another input type.

The roles of min, max, and step

min and max do not necessarily prevent typing out-of-range numbers. Even if an invalid value can be entered, ordinary form submission is blocked by validation. Add required if the value must not be empty.

The step base is a valid min value if present, otherwise a valid HTML value attribute, or zero if neither exists. Valid values are an integer number of steps from that base. The default interval is 1, but a decimal base can yield valid decimal values, so the default does not universally prohibit decimals.

Configuration Examples of valid values Caution
min="0" step="1" 0, 1, 2 0.5 does not fit the interval.
min="0" step="0.01" 1, 1.2, 1.23 1.234 does not fit. This does not force a two-decimal-place display.
min="0" step="3" 0, 3, 6, 9 Multiples of three from a zero base.
min="1" step="3" 1, 4, 7, 10 The base is one, so these differ from multiples of three.
step="any" Various decimal values within the range Removes the step constraint; other range and required constraints remain.

Compare integer and decimal input in one example

Save this complete code as number.html and open it in a browser. Without a server, compare validation, submitted strings, and JavaScript numeric values for quantity and weight. This exercise defines weight as 0–100 kg in increments of 0.01 kg.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Numeric ranges and steps</title>
  <style>
    body { margin: 24px; font-family: sans-serif; }
    label { display: block; margin: 16px 0 6px; }
    input { width: 12rem; max-width: 100%; box-sizing: border-box; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <h1>Enter quantity and weight</h1>
  <form id="demo">
    <label for="quantity">Quantity (1–10, whole units)</label>
    <input id="quantity" name="quantity" type="number"
      min="1" max="10" step="1" value="2" required>
    <label for="weight">Weight (0–100 kg, steps of 0.01 kg)</label>
    <input id="weight" name="weight" type="number"
      min="0" max="100" step="0.01" value="1.25" required>
    <p><button type="submit">Show values</button>
      <button type="reset">Reset</button></p>
  </form>
  <pre id="result" aria-live="polite"></pre>
  <script>
    const form = document.querySelector('#demo');
    const quantity = document.querySelector('#quantity');
    const weight = document.querySelector('#weight');
    const result = document.querySelector('#result');
    form.addEventListener('submit', (event) => {
      event.preventDefault();
      result.textContent = JSON.stringify({
        formData: [...new FormData(form).entries()],
        numbers: { quantity: quantity.valueAsNumber, weight: weight.valueAsNumber }
      }, null, 2);
    });
    form.addEventListener('input', () => { result.textContent = ''; });
    form.addEventListener('reset', () => { result.textContent = ''; });
  </script>
</body>
</html>
  1. Submit initially: formData contains strings "2" and "1.25", while numbers contains numeric 2 and 1.25.
  2. Set quantity to 11 and submit to trigger maximum validation. Set it to 2.5 for step validation, or clear it for required validation. Restore quantity to 2.
  3. A weight of 1.23 submits, but 1.234 does not fit the interval. Fixed decimal-place formatting differs from valid numeric intervals.
  4. Change only quantity’s step="1" to step="3" and reopen the file. Because min="1", initial value 2 is invalid but 4 is valid. Restore step to 1 afterward.
  5. Reset returns quantity to 2 and weight to 1.25 and clears the result.

Distinguish numeric-looking identifiers

Phone numbers, postal codes, and membership IDs often need their original string preserved rather than arithmetic. Leading zeros or hyphens may matter. Consider type="tel" for phones and type="text" inputmode="numeric" for numeric-looking identifiers. inputmode hints at a keyboard; it is not a numeric validation rule.

Number inputs do not use maxlength or pattern to constrain digit counts or regular-expression patterns. Set width with CSS instead of size. Browsers differ in character-entry behavior, increment arrows, and decimal-separator display, so do not rely on one appearance or blocking behavior.

Handle numbers in JavaScript and on the server

input.value and ordinary form-submitted values are strings. Use valueAsNumber for a number; it returns NaN when empty or not convertible. Converting an empty string with Number() produces zero, so distinguish an empty value first if it means something different from zero.

Browser validation alone does not make a value trustworthy. Validate numeric format, finiteness, range, and step on the server too. For calculations requiring decimal accuracy, such as amounts, account for JavaScript binary floating-point error and use integers in the smallest currency unit or an appropriate decimal approach.

The MDN number input reference explains attributes and step-base rules.