Choosing inputmode and enterkeyhint

Which keyboard should appear for a card number, a price, a verification code, a phone number or a URL — and what should the Enter key say and do on each field? Two attributes decide it. inputmode chooses the virtual keyboard without changing the field’s validation semantics, and enterkeyhint labels the Enter key (“next”, “done”, “go”, “send”). Chosen well, they prevent whole categories of validation errors before any rule runs; chosen badly, they force users to hunt for a decimal point or submit a half-finished form. This recipe maps common fields to the right combination of type, inputmode and enterkeyhint, explains when type should do the job instead, and wires Enter so its behaviour matches its label, while the form still validates through reportValidity() from the Constraint Validation API.

When to Use inputmode Instead of type

type and inputmode overlap, and the choice matters for validation:

  • Use type when you want the browser’s semantics and validation: type="email" checks email syntax, type="url" checks for a scheme, type="tel" gives a phone keypad and autofill without validating (numbering plans vary), type="number" for true quantities with spinners and min/max/step.
  • Use inputmode on type="text" when you want a keyboard without the semantics: identifiers made of digits (card numbers, codes, account numbers, postcodes in some countries), decimals that should not become number values, and URLs typed without a scheme.

The rule of thumb: if leading zeros, formatting or length matter more than numeric value, it is text with inputmode="numeric", not type="number". The reasoning for card fields is in payment card validation, and the broader mobile context in mobile form validation UX.

Keyboard and Enter key per field A table mapping common form fields to the recommended type, inputmode and enterkeyhint values. type inputmode enterkeyhint Email email (implied) next Card number text numeric next Price / amount text decimal next One-time code text numeric done Phone tel (implied) next Website text url next Search search (implied) search
Digits that are identifiers use text with a numeric keyboard; true quantities can use type number; Enter is labelled by position in the form.

Minimal Working Keyboard Setup

<form id="pay" novalidate>
  <label for="email">Email address</label>
  <input id="email" name="email" type="email" autocomplete="email"
         autocapitalize="none" spellcheck="false" enterkeyhint="next" required>

  <label for="amount">Amount (£)</label>
  <input id="amount" name="amount" type="text" inputmode="decimal"
         pattern="\d+([.,]\d{1,2})?" enterkeyhint="next" required>

  <label for="card">Card number</label>
  <input id="card" name="cardnumber" type="text" inputmode="numeric"
         autocomplete="cc-number" enterkeyhint="next" required>

  <label for="code">Security code</label>
  <input id="code" name="cvc" type="text" inputmode="numeric"
         autocomplete="cc-csc" maxlength="4" enterkeyhint="go" required>

  <button type="submit">Pay</button>
</form>
const form = document.querySelector<HTMLFormElement>("#pay")!;

// Make Enter do what its label says: "next" moves on, the last field's "go" submits.
form.addEventListener("keydown", (event) => {
  if (event.key !== "Enter" || event.isComposing) return;         // don't hijack IME confirmation
  const target = event.target as HTMLInputElement;
  const hint = target.getAttribute("enterkeyhint");
  if (hint === "next") {
    event.preventDefault();
    const fields = [...form.querySelectorAll<HTMLInputElement>("input:not([disabled]):not([type=hidden])")];
    fields[fields.indexOf(target) + 1]?.focus();
  }
  // "go", "done", "send": let Enter submit through the normal handler below.
});

form.addEventListener("submit", (event) => {
  // Normalise decimal commas typed on European keyboards before validating.
  const amount = form.querySelector<HTMLInputElement>("#amount")!;
  amount.value = amount.value.replace(",", ".");
  if (!form.reportValidity()) event.preventDefault();
});

The event.isComposing check matters for users typing with an input method editor (Chinese, Japanese, Korean): their Enter press confirms a composition and must not move focus or submit. The decimal normalisation matters because inputmode="decimal" shows the locale’s decimal separator, which is a comma for much of Europe; the validation rule has to accept what the keyboard offers.

type number or text with inputmode? A decision tree for choosing between type number and a text input with inputmode for numeric fields, based on leading zeros, arithmetic meaning and formatting. Can the value start with 0 or contain spaces? yes text + inputmode=numeric no Is it a quantity you would add or compare? yes Do you need decimals in any locale? yes text + inputmode=decimal no type=number with min, max, step no text + inputmode=numeric
If the value is an identifier rather than a quantity, it is text with a numeric keyboard.

inputmode and enterkeyhint Reference

Attribute Value Keyboard / label Typical fields
inputmode numeric Digits only Card numbers, codes, account numbers
inputmode decimal Digits plus locale decimal separator Prices, weights, measurements
inputmode tel Phone keypad with + * # Phone numbers (or use type="tel")
inputmode email @ and . prominent When type="email" is not wanted
inputmode url / and .com Website fields typed without a scheme
inputmode search Enter labelled for search Search boxes
inputmode none No virtual keyboard Fields with a custom on-screen picker only
enterkeyhint next / previous “Next” / “Previous” Every field except the last
enterkeyhint done / go / send / search Action label Final field of a form

inputmode="none" deserves caution: it hides the keyboard entirely, which is only appropriate when the component provides its own complete input method, such as a custom date picker. Used carelessly it makes a field impossible to type into.

Verification Steps

import { test, expect } from "@playwright/test";

test("Enter on a 'next' field moves focus instead of submitting", async ({ page }) => {
  let submitted = false;
  page.on("request", (r) => { if (r.method() === "POST") submitted = true; });
  await page.goto("/pay");
  await page.getByLabel("Email address").fill("ada@example.com");
  await page.getByLabel("Email address").press("Enter");
  await expect(page.getByLabel("Amount (£)")).toBeFocused();
  expect(submitted).toBe(false);
});

Edge Cases and Failure Modes

type="number" side effects. Number inputs change value when a user scrolls over them with a mouse, reject non-numeric characters silently (the value becomes "" and badInput is set), and strip leading zeros from what you read. For identifiers these are bugs; for quantities, add min, max, step and handle badInput explicitly.

inputmode does not validate. A numeric keyboard can still produce letters through paste, hardware keyboards and autofill. Keep a pattern or script rule and normalise input — spaces in card numbers, commas in decimals — before validating.

Enter labelled “next” but submitting. If you set enterkeyhint="next" without handling Enter, the browser still submits the form, contradicting the label. Label and behaviour must be changed together.

Search fields inside forms. A search box embedded in a larger form (for example, an address lookup) should use enterkeyhint="search" and handle Enter itself, so pressing it runs the lookup instead of submitting the whole form half-completed.

Keyboards that ignore the hint. Third-party keyboards on Android sometimes ignore inputmode or enterkeyhint. Treat both as enhancements: the field must still accept and validate whatever the user manages to type.

Hardware keyboards on tablets. inputmode has no effect when a physical keyboard is attached, so validation must never assume a restricted character set.

Text Handling Attributes That Pair With inputmode

The keyboard is only half of the typing experience on mobile; the other half is what the keyboard does to text as it is typed. Three attributes control it, and each prevents a class of validation errors. autocapitalize decides whether the first letter of each sentence or word is capitalised — essential to turn off for emails, usernames, codes and URLs, where “Ada@example.com” or “Promo2026” would fail case-sensitive checks or look wrong. autocorrect (honoured by Safari) and spellcheck stop the keyboard replacing identifiers with dictionary words — a username “adalove” silently becoming “adorable” is a real report, not a joke. And autocomplete connects the field to saved data, which on mobile is the single largest error-prevention feature: an autofilled email or address has no typos. The combination for an identifier field is therefore inputmode for the keyboard, autocapitalize="none" autocorrect="off" spellcheck="false" for the text, and the correct autocomplete token, as described in autocomplete attribute and validation. Name fields are the exception: leave capitalisation on for them (autocapitalize="words"), because users expect their names to start with capitals.

Handling Enter in Multi-Line Fields and Last Fields

Textareas are different: Enter inserts a new line, and hijacking it to move focus breaks writing. Leave Enter alone in textareas and rely on the submit button (or a modifier shortcut submitted through requestSubmit(), as in using requestSubmit() to trigger validation). For the last single-line field, enterkeyhint="done" or go tells users that Enter will finish the form; the submit handler then validates everything, and if a field higher up is invalid, reportValidity() scrolls to it and focuses it — which on mobile also reopens the keyboard for that field, exactly where the user needs it. Test that journey on a real phone: Enter on the last field with an earlier error should land the user on the earlier field with its message visible above the keyboard, as covered in validating forms with the virtual keyboard open.

Enter through a mobile form The user presses Enter on each field; next moves focus to the following field, and go on the last field submits, where validation finds an earlier error and focuses it. User Form fields Submit handler Enter on email (next) focus → amount Enter on card (next) Enter on security code (go) reportValidity → focus amount (invalid)
Enter is navigation until the last field; submission then validates everything and takes the user back to any problem.

Frequently Asked Questions

What is the difference between inputmode and type?

type sets the input's semantics and built-in validation, such as email syntax or numeric values. inputmode only chooses the virtual keyboard, so a text input with inputmode="numeric" shows a digit keypad without number semantics.

Should card numbers use type number?

No. Use type="text" with inputmode="numeric" and autocomplete="cc-number". Number inputs drop leading zeros, react to scrolling and show spinners, none of which suit identifiers.

What does enterkeyhint do?

It changes the label on the virtual keyboard's Enter key to next, done, go, send, search or previous. It does not change behaviour, so handle Enter in script to match the label.

How do I accept decimal commas on mobile?

inputmode="decimal" shows the locale's decimal separator. Accept both comma and point in your pattern or rule, and normalise the value before validating and submitting.

← Back to Mobile Form Validation UX