Make your AI a shadcn expert

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.

Scroll to load preview...

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

InputOTPSlot
InputOTPSlot
InputOTPSlot
InputOTPSeparator

Pattern

pattern={REGEXP_ONLY_DIGITS} rejects letters as they type.

Scroll to load preview...

Separator

InputOTPSeparator is a minus between groups. Two-two-two is easier to scan than six in a row.

Scroll to load preview...

Disabled

disabled greys the whole group. The value stays visible.

Scroll to load preview...

Controlled

value plus onChange. The string is the digits, not an array.

Scroll to load preview...

Invalid

aria-invalid on every slot. The root does not paint the error ring.

Scroll to load preview...

Four Digits

maxLength={4} and four slots. Pair with REGEXP_ONLY_DIGITS for a PIN.

Scroll to load preview...

Alphanumeric

REGEXP_ONLY_DIGITS_AND_CHARS for backup codes that mix letters and numbers.

Scroll to load preview...

Form

Wire the slots through Field. The error sits under the group.

Scroll to load preview...

RTL

Wrap with DirectionProvider and dir="rtl". Put the same dir on InputOTP.

Scroll to load preview...

API

Prop

Type

InputOTPSlot

Prop

Type

InputOTPGroup is a flex row. InputOTPSeparator is a minus with role="separator".

Keyboard

KeyAction
Digit / letterFill the active slot, move forward
BackspaceClear the slot, move back
ArrowLeft / ArrowRightPrevious / next slot
Cmd/Ctrl + VPaste 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

Questions

Was this page helpful?

Sign in to leave feedback.