Components

Input OTP

A single-field code entry for one-time passwords and verification flows, rendered as a row of individually styled slots with full keyboard, paste, and autofill support.

Basic usage

Set maxLength and render one InputOTPSlot per character inside an InputOTPGroup.

With separator

Split the slots into multiple InputOTPGroup blocks and place an InputOTPSeparator between them to visually chunk the code.

Digits only

Pass a pattern to constrain input — here a four-digit PIN that rejects any non-numeric key.

Disabled

The native disabled attribute blocks interaction and dims the entire container.

1
2
3
4
5
6

Props

InputOTP — forwards every prop of the underlying input-otp field.

PropTypeDefaultDescription
maxLengthrequirednumberNumber of characters in the code. Must match the highest slot index + 1.
valuestringControlled value of the input. Omit for an uncontrolled field.
defaultValuestringInitial value when the input is uncontrolled.
onChange(value: string) => voidFires with the full string each time the value changes.
onComplete(value: string) => voidFires once every slot is filled — ideal for auto-submitting a code.
patternstringRegex restricting allowed characters, e.g. REGEXP_ONLY_DIGITS.
disabledbooleanfalseDisables the field and dims the container to 50% opacity.
containerClassNamestringClasses merged onto the flex container wrapping the groups.
classNamestringClasses merged onto the underlying hidden input element.

InputOTPSlot — one slot per character.

PropTypeDefaultDescription
indexrequirednumberZero-based position of the slot within the code.
classNamestringAdditional Tailwind classes to merge onto the slot.

Accessibility

  • Backed by a single real <input> element, so screen readers, password managers, and SMS autofill (autocomplete="one-time-code") all work out of the box.
  • Full keyboard support: arrow keys move between slots, Backspace clears and steps back, and pasting a full code fills every slot at once.
  • The active slot is marked with data-active and shows a focus ring plus a blinking caret, giving a clear visual indication of caret position.
  • The separator carries role="separator" so it is announced as a divider rather than as content.