InputOTP
input-otpCollects one-time codes across fixed-length slots with auto-advance, backspace, and full-code paste.
Usage
Basic usage
6-digit verification code, automatically assigned to skip spaces, backspaces, and paste the entire paragraph.
<InputOTP length={6} value={otp} onChange={setOtp} onComplete={verify} />Group separation
groupGap Insert a horizontal line in the middle to present a 3-3 grouping vision.
<InputOTP length={6} groupGap value={otp} onChange={setOtp} />Any character
type="text" Accepts alphanumeric redemption codes/invitation codes.
<InputOTP length={6} type="text" value={code} onChange={setCode} />Invalid state
invalid is marked with a red border and prompts you to re-enter when the verification fails.
<InputOTP length={4} invalid value={otp} onChange={setOtp} />When to use
Use InputOTP for fixed-length SMS or email codes, PINs, and two-factor codes. It handles segment focus, Backspace, full-code paste and splitting, and onComplete; do not recreate this with separate text inputs and regular expressions. Use Input or SecretField for unrestricted text.
Import
import { InputOTP } from "@hulianui/ui"Props
Inherits the native attributes of the root role="group" element, so id, data-*, aria-*, onFocus, and onBlur can all be passed directly.
| Name | Type | Default | Description |
|---|---|---|---|
| length | number | 6 | number of segments |
| value | string | - | controlled value |
| defaultValue | string | "" | Uncontrolled initial value. |
| type | "numeric" | "text" | "numeric" | Numbers only (default) or any characters |
| disabled | boolean | false | Disables the control. |
| invalid | boolean | false | Verification failed status |
| groupGap | boolean | false | Inserts a separator in the middle for a 3-3 grouping such as XXX-XXX. |
| name | string | - | Submission name. Renders an extra hidden input holding the complete value, because the segments each hold a single character and would otherwise submit N separate fields. |
| className | string | - | Container class name |
| aria-label | string | "\u9a8c\u8bc1\u7801" | Accessible name; the built-in Chinese copy means “Verification code.” |
Events
| Event | Type | Description |
|---|---|---|
| onChange | (value: string) => void | value change callback |
| onComplete | (value: string) => void | Callback when full |
| onBlur | (e: FocusEvent<HTMLDivElement>) => void | Fires when focus leaves the whole group; moving between segments does not count. Pass field.onBlur here when using a react-hook-form Controller. |
Usage guidelines
onCompletefires once when the final segment is filled. Put verification there instead of repeatedly checkingvalue.length === lengthinonChange.- Pair a controlled
valuewithonChange; without the update, the segments cannot accept input. - With react-hook-form, the value is a single string rather than a native input, so it must go through
Controller, andfield.onBlurmust be passed in. Without ittouchedFieldsnever updates and a form usingmode: "onBlur"or"onTouched"fails silently: focusing and leaving the field never triggers validation, and errors only appear on submit. onBlurhas whole-group semantics: moving focus between segments does not fire it. Per-segment blur has to be handled separately.
Related
SecretField · Combobox · Listbox · Mentions · Rating · Upload
Playground
<InputOTP
length={6}
type="numeric"
value={otp}
onChange={setOtp}
onComplete={verify}
/>