Manual UI Templates InputOTP

InputOTP

elements man ui/templates/input-otp

A one-time code in separate slots, with paste, SMS autofill and a pattern.

Example
import { InputOTP, REGEXP_ONLY_DIGITS } from "@elements/ui/input-otp"; <InputOTP value={form.code} maxLength={6} groups={[3, 3]} pattern={REGEXP_ONLY_DIGITS} /> <InputOTP value={form.pin} maxLength={4} onComplete={(code) => verify(code)} />

Parts

Part What it is
.input-otp The root.
.input-otp-input The real input, transparent over the slots.
.input-otp-group A run of joined slots.
.input-otp-slot One character.
.input-otp-separator Between groups.
.input-otp-caret The blinking caret in an empty active slot.

Attributes

Attribute Type Default Description
value string (two-way) "" The code.
maxLength number 6 Number of slots.
groups number[] [] Slots per group, e.g. [3, 3]. A separator goes between groups.
pattern string | RegExp "" Every change must match. REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS and REGEXP_ONLY_DIGITS_AND_CHARS are exported.
pasteTransformer ((pasted: string) => string) | null null Cleans pasted text first, e.g. removes dashes.
inputmode "numeric" | "text" | "decimal" | "tel" | "email" | "url" | "search" | "none" "numeric" Which keyboard a phone shows.
autocomplete string "one-time-code" Lets the browser fill a code from an SMS.
placeholder string "" One placeholder character per empty slot.
pushPasswordManagerStrategy "increase-width" | "none" "increase-width" Keeps a password manager's badge off the last slot.
textAlign "left" | "center" | "right" "left" Where the hidden input's text sits, which is where a click-hold or long press lands (input-otp textAlign).
inputClass string "" Classes added to the real input (input-otp className).
size "sm" | "md" | "lg" "md" Slot size from the control heights.
name string "" Submits the code with its form.
disabled boolean false Not interactive.
readonly boolean false Focusable and submitted, but typing and paste do nothing.
required boolean false The form will not submit until every slot is filled.
invalid boolean false Danger borders on the slots.
class string "" Extra classes on the root.
attrs Record<string, string | number | boolean | undefined> {} Attributes for the input: id, aria-label, autofocus, data-*.
onValueChange (value: string) => void () => {} Called with the new code on every change the user makes (input-otp onChange).
onComplete (value: string) => void () => {} Called once when the last slot fills.

Slots

Slot Content
separator Shown between groups. Default: a dash.

Keyboard

Keys Action
Type Fills the active slot and moves on.
Backspace Deletes the active slot's character.
ArrowLeft / ArrowRight Moves between slots.
Home / End Moves to the first slot, or the slot after the code.
Ctrl/Cmd + V Pastes a whole code.

Data attributes

Attribute Values On
data-active present on the slot being edited slot
data-filled present when the slot has a character slot
data-placeholder present while a placeholder shows slot
data-index the slot's index slot
data-complete present when every slot is filled root
data-disabled present when disabled root
data-readonly present when read-only root
data-invalid present when invalid root

Accessibility

  • It is one text input, so a screen reader reads and edits the code as one field; the slots are drawing only (aria-hidden).
  • autocomplete="one-time-code" lets iOS and Android offer the code from an SMS, and a pasted code fills every slot.
  • Name it with a Field or aria-label through attrs.

See also