Input OTP
Forms
Security
One-Time Password input for verification codes, PIN codes, and security authentication. Features auto-focus, paste support, and keyboard navigation.
Preview
6-digit verification code input
With Separator
Visual separator between digit groups (e.g., 123-456)
PIN Code
4-digit PIN input with password masking
Text Type
Accept alphanumeric characters
Error State
Show error when validation fails
Try entering "123456" for success, or any other code to see error state.
Props
InputOTP component API reference
| Prop | Type | Default | Description |
|---|---|---|---|
length | number | 6 | Number of input fields |
value | string | '' | Controlled value |
onChange | (value: string) => void | — | Callback when value changes |
onComplete | (value: string) => void | — | Callback when all fields filled |
type | 'number' | 'text' | 'password' | 'number' | Input type and keyboard |
separator | boolean | false | Show separator between groups |
separatorAfter | number | 3 | Position of separator |
label | string | — | Label text above inputs |
error | boolean | string | false | Error state and message |
disabled | boolean | false | Disable all inputs |
className | string | — | Additional CSS classes |
Usage
Import and implementation example
import { InputOTP } from '@/components/ui/input-otp'
export default function Example() {
const [code, setCode] = useState('')
const [error, setError] = useState<string | false>(false)
const handleComplete = async (value: string) => {
try {
await verifyCode(value)
// Success - redirect or show success state
} catch (err) {
setError('Invalid code. Please try again.')
}
}
return (
<>
{/* Basic 6-digit code */}
<InputOTP
label="Verification Code"
length={6}
value={code}
onChange={setCode}
onComplete={handleComplete}
error={error}
/>
{/* With separator */}
<InputOTP
label="Enter Code"
length={6}
value={code}
onChange={setCode}
separator
separatorAfter={3}
/>
{/* 4-digit PIN */}
<InputOTP
label="PIN Code"
length={4}
value={code}
onChange={setCode}
type="password"
/>
{/* Alphanumeric */}
<InputOTP
label="License Key"
length={6}
value={code}
onChange={setCode}
type="text"
/>
</>
)
}Features
Built-in functionality
- Auto-focus next: Automatically moves to next field on entry
- Backspace navigation: Move to previous field on backspace
- Arrow key navigation: Navigate between fields with arrow keys
- Paste support: Paste entire code to fill all fields
- Optional separator: Visual separator between digit groups
- Three input types: Number, text, or password (masked)
- Completion callback: Triggers when all fields are filled
- Error state: Custom error messages with visual feedback
- Configurable length: 4, 6, 8 or any number of digits
- Dark mode: Full dark mode support
Accessibility
Accessibility considerations
ARIA Attributes
Each input is properly labeledError messages linked via aria-describedbyInput mode set for appropriate keyboardKeyboard Navigation
| Key | Action |
|---|---|
| Tab | Focus first/next input field |
| Arrow Left/Right | Navigate between fields |
| Backspace | Clear current field, move to previous |
| Paste | Fill all fields from clipboard |
Notes
- Focus automatically advances on input
- Error state communicated visually and semantically
- Password type masks input for security
- Numeric keyboard shown on mobile for number type