Input OTP React component
A one-time-code input with individual animated cells, paste and autofill support, numeric or alphanumeric mode, masking, grouped cells, error shake and success states.
About the Input OTP component
Input OTP is a one-time code field drawn as a row of separate boxes, one per character. A blinking caret marks the next box, characters pop in as they are typed, and the boxes turn green on success or red with a short shake on error. Under the boxes it is a single real input with autocomplete="one-time-code", so SMS code autofill on phones and pasting a whole code both fill every box at once.
length sets the number of boxes (6 by default) and type picks numeric, which also opens the number keypad on phones, or alphanumeric. value or defaultValue with onValueChange hold the code, and onComplete fires when the last box is filled. mask hides the characters as dots, groupSize splits the boxes into groups such as 3 and 3, and status with errorText or helperText shows the result. label, size, autoFocus and disabled set the rest.
When to use it
Use Input OTP for one-time passcodes: two-factor login, email verification and phone confirmation. The digits appear in separate boxes, but under the hood it's a single input with autocomplete="one-time-code", so SMS autofill on iOS and Android and pasting a whole code both just work.
Use onComplete to verify the code the moment the last digit is entered, and the error and success states to show the result in place.
Installation
Add the Input OTP component to your project with the shadcn CLI. It lands in components/input-otp.tsx as plain React + Tailwind CSS source you own and can edit.
npx shadcn@latest add @reactframe/input-otpOr copy the source by hand and install its dependencies:
npm install motion clsx tailwind-mergeProps
<InputOTP /> accepts 17 props, all optional.
| Prop | Type | Default |
|---|---|---|
| length | number | 6 |
| value | string | — |
| defaultValue | string | "" |
| onValueChange | (value: string) => void | — |
| onComplete | (value: string) => void | — |
| type | "numeric" | "alphanumeric" | "numeric" |
| mask | boolean | false |
| groupSize | number | — |
| status | "idle" | "error" | "success" | "idle" |
| label | string | — |
| helperText | string | — |
| errorText | string | — |
- lengthnumberDefault: 6
- valuestring
- defaultValuestringDefault: ""
- onValueChange(value: string) => void
- onComplete(value: string) => void
- type"numeric" | "alphanumeric"Default: "numeric"
- maskbooleanDefault: false
- groupSizenumber
- status"idle" | "error" | "success"Default: "idle"
- labelstring
- helperTextstring
- errorTextstring
Show all 17 propsShow fewer
| size | "sm" | "md" | "lg" | "md" |
| theme | "dark" | "light" | "dark" |
| autoFocus | boolean | false |
| disabled | boolean | false |
| className | string | — |
- size"sm" | "md" | "lg"Default: "md"
- theme"dark" | "light"Default: "dark"
- autoFocusbooleanDefault: false
- disabledbooleanDefault: false
- classNamestring
npm dependencies
Installed for you by the shadcn CLI.
Input OTP FAQ
- Does pasting a full code work?
- Yes. It's one real input behind the boxes, so pasting fills every box at once and onComplete fires.
- Can the code contain letters?
- Yes. Set type="alphanumeric"; the default numeric type also brings up the number keypad on phones.
- How do I show that the code was wrong?
- Set status="error" and pass errorText; status="success" shows the verified state.
- How do I split a 6-digit code into two groups?
- Set groupSize={3}. A small divider is drawn between every 3 boxes, which makes long codes easier to read off a phone screen. The value stays one plain string.