One-time code input for verification and two-factor sign in. One real input holds the code and drawn cells show it, so paste, autofill from SMS and screen readers all work.
import { InputOTP } from "@rdloom/react";
export default function InputOTPBasicExample() {
return <InputOTP label="Verification code" description="Enter the 6 digit code we sent to your phone." onComplete={() => {}} />;
}Installation
npx rdloom add input-otpCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/input-otp.jsonWorks, but without upgrade tracking.
Usage
import { InputOTP } from "@rdloom/react";
<InputOTP label="Verification code" description="Enter the 6 digit code we sent to your phone." onComplete={() => {}} />Alphanumeric grouped
import { InputOTP } from "@rdloom/react";
export default function InputOTPAlphanumericGroupedExample() {
return <InputOTP label="Backup code" type="alphanumeric" separatorAt={[3]} description="Letters and digits, in two groups of three." />;
}With error
import { InputOTP } from "@rdloom/react";
export default function InputOTPWithErrorExample() {
return <InputOTP label="Verification code" defaultValue="123456" isInvalid errorMessage="That code is not right. Check it and try again." />;
}Masked
import { InputOTP } from "@rdloom/react";
export default function InputOTPMaskedExample() {
return <InputOTP label="Security PIN" length={4} mask description="Your 4 digit PIN is hidden as you type." />;
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
labelrequiredVisible label, such as Verification code. Names the group. | string | none |
descriptionHelp text shown under the cells. | string | none |
errorMessageShown when the code is invalid. | string | none |
lengthNumber of characters in the code. | number | 6 |
typeCharacters accepted. Numeric also opens the numeric keypad on phones. | "numeric" | "alphanumeric" | "numeric" |
valueControlled value. | string | none |
defaultValueInitial value when uncontrolled. | string | none |
onChangeCalled with the code on every edit. | (value: string) => void | none |
onCompleteCalled once when every cell is filled. | (value: string) => void | none |
separatorAtDraws a separator after these cell counts, for example [3] groups a 6 digit code as 3 + 3. | number[] | none |
maskShows dots instead of the characters. | boolean | false |
autoFocusFocuses the input on mount. | boolean | false |
isDisabledPrevents editing and dims the cells. | boolean | false |
isInvalidMarks the code as wrong and shows errorMessage. | boolean | false |
Accessibility
Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Type to fill and move forward
- Backspace deletes and moves back
- Left and Right Arrow move between cells
- Paste fills every cell
- Tab moves out
Screen readers announce
- On focus: the label, then edit text, then the description
- Moving between cells announces the position, such as Digit 2 of 6
- Invalid: announced as invalid, and the error message is read
- Masked codes are announced as a protected field and the characters are never read aloud
What your code must do
- The group is named by its label
- One real input carries the value, so autofill and paste work
- autocomplete=one-time-code lets phones offer the SMS code
- The current cell is announced as digit N of the total
- The error is linked with aria-describedby and the input exposes aria-invalid
Guidelines
Use it when
- Verifying an SMS, email or authenticator code
- Any short fixed-length code
Avoid it when
- Free-form or variable-length text: use TextField
- Long recovery keys: use TextField
Don't
- Splitting the code into separate inputs, which breaks paste and autofill
- Clearing the code silently on error: say what went wrong
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-surface-default--rd-color-border-default--rd-color-border-strong--rd-color-text-default--rd-color-text-muted--rd-color-feedback-danger--rd-color-focus-ring--rd-radius-control