Shadcn Input OTP
React OTP input for 2FA codes, PINs, and SMS verification. Built with TypeScript and Tailwind CSS for Next.js using Base UI — paste a code and every slot fills.
The shadcn OTP input is one hidden field painted as boxes. Type a digit and the caret jumps; paste a code and every slot fills. Use it for 2FA, SMS codes, and PIN entry — not for passwords.
Looking for a neobrutalism version for shadcn?
Same input otp, thicker borders. The NeoBrutalism input otp is the kit if you want that look.
Usage
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp"<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>maxLength must match the number of slots. pattern is a regex string — REGEXP_ONLY_DIGITS for an SMS code.
Composition
Pattern
pattern={REGEXP_ONLY_DIGITS} rejects letters as they type.
Separator
InputOTPSeparator is a minus between groups. Two-two-two is easier to scan than six in a row.
Disabled
disabled greys the whole group. The value stays visible.
Controlled
value plus onChange. The string is the digits, not an array.
Invalid
aria-invalid on every slot. The root does not paint the error ring.
Four Digits
maxLength={4} and four slots. Pair with REGEXP_ONLY_DIGITS for a PIN.
Alphanumeric
REGEXP_ONLY_DIGITS_AND_CHARS for backup codes that mix letters and numbers.
Form
Wire the slots through Field. The error sits under the group.
RTL
Wrap with DirectionProvider and dir="rtl". Put the same dir on InputOTP.
API
Prop
Type
InputOTPSlot
Prop
Type
InputOTPGroup is a flex row. InputOTPSeparator is a minus with role="separator".
Keyboard
| Key | Action |
|---|---|
| Digit / letter | Fill the active slot, move forward |
Backspace | Clear the slot, move back |
ArrowLeft / ArrowRight | Previous / next slot |
Cmd/Ctrl + V | Paste across slots |
This is one hidden input. Arrow keys, select-all, and iOS SMS autofill work like a normal field.
Notes
Invalid is per slot
aria-invalid on every InputOTPSlot. Putting it on InputOTP does nothing to the rings.
maxLength must equal the number of slots. Label InputOTP with id — the slots are paint.
With other components
Label the code
FormSubmit on complete
ButtonResend the code
DialogVerify in a modal
CardFrame the sign-in
InputEmail before the code
Questions
Related
Was this page helpful?
Sign in to leave feedback.
Input Group
React input group for icons, buttons, and addons on inputs. Built with TypeScript and Tailwind CSS for Next.js using Base UI — one focus ring for the whole control.
Item
React item for settings, notification, and file list rows. Built with TypeScript and Tailwind CSS for Next.js using Base UI — media, title, description, and actions.