Components/Forms
One-time code input with auto-advance, paste and backspace handling.
Check your inbox
Enter the 6-digit code we sent to ada@northwind.io
Didn't get it?
Installation#
npx shadcn@latest add https://get-neonkit.vercel.app/r/otp-input.jsonUsage#
import { OTPInput } from "@/components/ui/otp-input"
<OTPInput onComplete={(code) => verify(code)} />Examples#
Grouped
groupSize splits the slots with a dash, so long codes are easier to read back.
Pair your TV
Type the code shown on the screen.
Masked PIN
mask shows dots instead of digits.
Approve production deploy
Enter your 4-digit PIN to ship v2.4.0.
Invalid code
The row shakes and turns red each time invalid switches on.
Two-factor authentication
Open your authenticator app. Anything but 424242 fails.
Letters and numbers
mode="alphanumeric" accepts letters too and brings up the full keyboard on phones.
Use a recovery code
One of the 8-character codes you saved when you set up 2FA.
API reference#
OTPInput
Other props and the ref go to the wrapping group element.
| Prop | Type | Default |
|---|---|---|
length | numberNumber of slots. | 6 |
value | stringControlled code. | — |
defaultValue | stringInitial code when uncontrolled. | — |
onValueChange | (code: string) => voidCalled on every change. | — |
onComplete | (code: string) => voidCalled when the last empty slot is filled. | — |
mode | "numeric" | "alphanumeric"Characters accepted. Also picks the mobile keyboard. | "numeric" |
groupSize | numberPuts a dash after every groupSize slots. | — |
mask | booleanShows dots instead of characters. | false |
invalid | booleanRed state. Shakes the row when it switches on. | false |
disabled | booleanDisables every slot. | false |
autoFocus | booleanFocuses the first empty slot on mount. | false |
name | stringSubmits the code with a surrounding form. | — |
aria-label | stringNames the group of slots. | "One-time code" |
color | NeonColorPalette name or hex. Falls back to --neon-accent, then cyan. | inherited |
size | "sm" | "md" | "lg"Slot size and text size. | "md" |
Accessibility#
- Each slot is a native input labelled "Digit 2 of 6" inside a named group.
- The first slot uses autocomplete="one-time-code", so codes from SMS can fill every slot in one tap.
- Pasting fills from the focused slot. A full-length code always fills from the start, and spaces or dashes are dropped.
- Only the next slot to fill is in the tab order. Arrow keys, Home and End move between slots.
- Backspace clears the focused slot, or steps back and clears the previous one when the focused slot is empty.
- Link an error message with aria-describedby when invalid is on. The shake is skipped when reduced motion is on.