Input OTP
Accessible one-time password component with copy paste functionality.
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
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.
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-describedbyto 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>Related Components
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.
| Prop | Type | Default | Description |
|---|---|---|---|
| maxLength | number | — | Maximum number of characters (required). |
| value | string | — | The controlled value of the OTP input. |
| onChange | (value: string) => void | — | Event handler called when the value changes. |
| pattern | string | RegExp | digits only | Pattern to validate input characters. |
| disabled | boolean | false | When true, prevents user interaction. |
| containerClassName | string | — | Additional CSS classes for the outer container element. |
| className | string | — | Additional CSS classes to apply to the input. |
InputOTPGroup
Groups slots together visually with shared border rounding.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Additional CSS classes for the group container. |
InputOTPSlot
Individual character slot that displays the entered character and focus caret.
| Prop | Type | Default | Description |
|---|---|---|---|
| index | number | — | The zero-based index of this slot within the OTP input (required). |
| className | string | — | Additional CSS classes for the slot element. |
InputOTPSeparator
Visual separator between groups. Renders a minus icon by default.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | <MinusIcon /> | Custom separator content. Defaults to a minus icon. |
| className | string | — | Additional CSS classes for the separator element. |