Inputs/Input OTP

Input OTP

Accessible one-time password component with copy paste functionality.

Themed

When to Use

Use Input OTP when you need to:

  • Collect one-time passwords or verification codes sent via SMS or email
  • Capture short numeric or alphanumeric codes for two-factor authentication
  • Accept PIN entry with a fixed number of digits
  • Provide a structured input for invite codes or license keys

When Not to Use

  • General text input or passwords - use Input
  • Multi-line text entry - use a standard textarea component
  • Variable-length codes where the user decides the length - use Input with a pattern
  • Form fields that need labels and validation wrappers - combine with Field and Label

Default

6-digit OTP input with separator between two groups of three.

Pattern

Accept alphanumeric characters with a custom validation pattern.

Multiple Separators

Use separators to create visual groupings such as a 2-2-2 format.

Controlled

Controlled OTP input with external state management and live value display.

Enter your one-time password.

Disabled

Disabled state prevents all interaction and applies reduced opacity.

UX & Design Guidelines

Visual Hierarchy

The OTP input should be the primary focus of the verification screen. Center it horizontally and pair it with a clear heading such as "Enter verification code". Use InputOTPSeparator to visually group digits (e.g., 3-3 or 2-2-2) matching the format users receive in their SMS or email.

Spacing & Layout

Each slot is h-9 w-9 by default with gap-2 between groups. Provide generous vertical spacing above and below the input for comfortable reading. Include helper text below the input to guide users (e.g., "Check your phone for the code").

Responsive Behavior

The OTP input scales well on mobile since individual slots provide large touch targets. On narrow screens, consider reducing maxLength grouping (e.g., 3-3 instead of 6 ungrouped) to prevent horizontal overflow. The component works well centered in both mobile and desktop layouts.

Color & Contrast

Active slots use border-ring with a ring-ring/50 focus ring for clear visual feedback. Invalid states apply border-destructive with a destructive ring color. The animated caret uses bg-foreground for maximum contrast against the slot background.

Accessibility

Keyboard Navigation

  • Tab — Move focus to/from the OTP input
  • 0-9 or A-Z — Enter a character and auto-advance to the next slot
  • Backspace — Delete the current character and move focus to the previous slot
  • ArrowLeft / ArrowRight — Navigate between slots without changing values
  • Ctrl+V — Paste a full code, automatically distributing characters across slots

Screen Reader Support

  • The input is announced as a single text field, not individual slots
  • Current value and position are communicated to assistive technologies
  • Always pair with a visible <Label> to provide context (e.g., "Verification Code")
  • Use aria-describedby to link helper text explaining where the code was sent

Focus Management

The active slot displays a visible focus ring with ring-[3px] and an animated caret indicator. Focus automatically advances to the next slot after input and returns to the previous slot on backspace. The component uses z-10 on the active slot to ensure the focus ring is never clipped by adjacent slots.

ARIA Attributes

{/* Associate with a label for form context */}
<Label htmlFor="otp-input">Verification Code</Label>
<InputOTP id="otp-input" 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>

{/* With description for additional context */}
<InputOTP maxLength={6} aria-describedby="otp-description">
  ...
</InputOTP>
<p id="otp-description" className="sr-only">
  Enter the 6-digit code sent to your phone
</p>

API Reference

The Input OTP component is composed of four sub-components that work together.

InputOTP

The root OTP input component. Accepts all props from the underlying input-otp library.

PropTypeDefaultDescription
maxLengthnumberMaximum number of characters (required).
valuestringThe controlled value of the OTP input.
onChange(value: string) => voidEvent handler called when the value changes.
patternstring | RegExpdigits onlyPattern to validate input characters.
disabledbooleanfalseWhen true, prevents user interaction.
containerClassNamestringAdditional CSS classes for the outer container element.
classNamestringAdditional CSS classes to apply to the input.

InputOTPGroup

Groups slots together visually with shared border rounding.

PropTypeDefaultDescription
classNamestringAdditional CSS classes for the group container.

InputOTPSlot

Individual character slot that displays the entered character and focus caret.

PropTypeDefaultDescription
indexnumberThe zero-based index of this slot within the OTP input (required).
classNamestringAdditional CSS classes for the slot element.

InputOTPSeparator

Visual separator between groups. Renders a minus icon by default.

PropTypeDefaultDescription
childrenReactNode<MinusIcon />Custom separator content. Defaults to a minus icon.
classNamestringAdditional CSS classes for the separator element.