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.
| Prop | Type | Default | Description |
|---|---|---|---|
maxLengthrequired | number | — | Number of characters in the code. Must match the highest slot index + 1. |
value | string | — | Controlled value of the input. Omit for an uncontrolled field. |
defaultValue | string | — | Initial value when the input is uncontrolled. |
onChange | (value: string) => void | — | Fires with the full string each time the value changes. |
onComplete | (value: string) => void | — | Fires once every slot is filled — ideal for auto-submitting a code. |
pattern | string | — | Regex restricting allowed characters, e.g. REGEXP_ONLY_DIGITS. |
disabled | boolean | false | Disables the field and dims the container to 50% opacity. |
containerClassName | string | — | Classes merged onto the flex container wrapping the groups. |
className | string | — | Classes merged onto the underlying hidden input element. |
InputOTPSlot — one slot per character.
| Prop | Type | Default | Description |
|---|---|---|---|
indexrequired | number | — | Zero-based position of the slot within the code. |
className | string | — | Additional 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-activeand 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.