Input OTP
A one-time password (OTP) input component built on the `input-otp` library. Perfect for verification codes, two-factor authentication, and secure input scenarios requiring specific character patterns.
Installation
pnpm add @helgadigitals/vera-uinpm install @helgadigitals/vera-uiyarn add @helgadigitals/vera-uiUsage
import {InputOTP,InputOTPGroup,InputOTPSlot,} from "@helgadigitals/vera-ui"export default function Example() {return ( <InputOTP maxLength={6}> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP>)}Examples
Basic OTP Input
Enter your one-time password
import { useState } from "react"import {InputOTP,InputOTPGroup,InputOTPSlot,} from "@helgadigitals/vera-ui"export default function BasicExample() {const [value, setValue] = useState("")return ( <div className="space-y-4"> <div className="space-y-2"> <label className="text-sm font-medium">Enter verification code</label> <InputOTP maxLength={6} value={value} onChange={(value) => setValue(value)} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </div> <div className="text-center text-sm text-muted-foreground"> {value === "" ? ( <>Enter your one-time password</> ) : ( <>You entered: {value}</> )} </div> </div>)}With Separator
Please enter the 6-digit code sent to your phone
import { useState } from "react"import {InputOTP,InputOTPGroup,InputOTPSlot,InputOTPSeparator,} from "@helgadigitals/vera-ui"export default function WithSeparatorExample() {const [value, setValue] = useState("")return ( <div className="space-y-4"> <div className="space-y-2"> <label className="text-sm font-medium">Enter SMS code</label> <InputOTP maxLength={6} value={value} onChange={(value) => setValue(value)} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </div> <p className="text-xs text-muted-foreground text-center"> Please enter the 6-digit code sent to your phone </p> </div>)}Different Lengths
import { useState } from "react"import {InputOTP,InputOTPGroup,InputOTPSlot,} from "@helgadigitals/vera-ui"export default function DifferentLengthsExample() {const [value4, setValue4] = useState("")const [value6, setValue6] = useState("")return ( <div className="space-y-8"> {/* 4-digit code */} <div className="space-y-2"> <label className="text-sm font-medium">4-digit PIN</label> <InputOTP maxLength={4} value={value4} onChange={(value) => setValue4(value)} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> </InputOTPGroup> </InputOTP> </div> {/* 6-digit code */} <div className="space-y-2"> <label className="text-sm font-medium">6-digit verification</label> <InputOTP maxLength={6} value={value6} onChange={(value) => setValue6(value)} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </div> </div>)}Disabled State
Please enable two-factor authentication to use this feature
import {InputOTP,InputOTPGroup,InputOTPSlot,} from "@helgadigitals/vera-ui"export default function DisabledExample() {return ( <div className="space-y-4"> <div className="space-y-2"> <label className="text-sm font-medium text-muted-foreground"> Verification code (disabled) </label> <InputOTP maxLength={6} disabled> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </div> <p className="text-sm text-muted-foreground"> Please enable two-factor authentication to use this feature </p> </div>)}Form Integration
import { useForm, Controller } from "react-hook-form"import {InputOTP,InputOTPGroup,InputOTPSlot,Button,} from "@helgadigitals/vera-ui"export default function FormExample() {const { control, handleSubmit, formState: { errors } } = useForm()const onSubmit = (data) => { console.log("Verification code:", data.code)}return ( <form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> <div className="space-y-2"> <label className="text-sm font-medium"> Verification Code * </label> <Controller name="code" control={control} rules={{ required: "Verification code is required", minLength: { value: 6, message: "Please enter the complete 6-digit code" } }} render={({ field }) => ( <InputOTP maxLength={6} value={field.value || ""} onChange={field.onChange} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> )} /> {errors.code && ( <p className="text-sm text-destructive"> {errors.code.message} </p> )} </div> <Button type="submit" className="w-full"> Verify Code </Button> </form>)}With Timer and Resend
Resend code in 1:00
import { useState, useEffect } from "react"import {InputOTP,InputOTPGroup,InputOTPSlot,Button,} from "@helgadigitals/vera-ui"export default function WithTimerExample() {const [value, setValue] = useState("")const [timeLeft, setTimeLeft] = useState(60)const [canResend, setCanResend] = useState(false)useEffect(() => { if (timeLeft > 0) { const timer = setTimeout(() => setTimeLeft(timeLeft - 1), 1000) return () => clearTimeout(timer) } else { setCanResend(true) }}, [timeLeft])const handleResend = () => { // Simulate sending new code console.log("Resending code...") setTimeLeft(60) setCanResend(false) setValue("")}const formatTime = (seconds) => { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins}:${secs.toString().padStart(2, '0')}`}return ( <div className="space-y-6"> <div className="space-y-2"> <label className="text-sm font-medium"> Enter verification code </label> <InputOTP maxLength={6} value={value} onChange={(value) => setValue(value)} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </div> <div className="text-center space-y-2"> {canResend ? ( <Button variant="outline" onClick={handleResend}> Resend Code </Button> ) : ( <p className="text-sm text-muted-foreground"> Resend code in {formatTime(timeLeft)} </p> )} </div> </div>)}Custom Styling
import { useState } from "react"import {InputOTP,InputOTPGroup,InputOTPSlot,} from "@helgadigitals/vera-ui"export default function CustomStylingExample() {const [value, setValue] = useState("")return ( <div className="space-y-8"> {/* Large slots */} <div className="space-y-2"> <label className="text-sm font-medium">Large OTP Input</label> <InputOTP maxLength={4} value={value} onChange={(value) => setValue(value)} > <InputOTPGroup> <InputOTPSlot index={0} className="w-12 h-12 text-lg" /> <InputOTPSlot index={1} className="w-12 h-12 text-lg" /> <InputOTPSlot index={2} className="w-12 h-12 text-lg" /> <InputOTPSlot index={3} className="w-12 h-12 text-lg" /> </InputOTPGroup> </InputOTP> </div> {/* Rounded slots */} <div className="space-y-2"> <label className="text-sm font-medium">Rounded OTP Input</label> <InputOTP maxLength={6} value={value} onChange={(value) => setValue(value)} > <InputOTPGroup> <InputOTPSlot index={0} className="rounded-full" /> <InputOTPSlot index={1} className="rounded-full" /> <InputOTPSlot index={2} className="rounded-full" /> <InputOTPSlot index={3} className="rounded-full" /> <InputOTPSlot index={4} className="rounded-full" /> <InputOTPSlot index={5} className="rounded-full" /> </InputOTPGroup> </InputOTP> </div> </div>)}Alphanumeric Code
Format: XXXX-XXXX (letters and numbers only)
import { useState } from "react"import {InputOTP,InputOTPGroup,InputOTPSlot,InputOTPSeparator,} from "@helgadigitals/vera-ui"const REGEXP_ONLY_CHARS_AND_DIGITS = "^[A-Z0-9]+$"export default function AlphanumericExample() {const [value, setValue] = useState("")return ( <div className="space-y-4"> <div className="space-y-2"> <label className="text-sm font-medium"> Enter backup code (letters and numbers) </label> <InputOTP maxLength={8} pattern={REGEXP_ONLY_CHARS_AND_DIGITS} value={value} onChange={(value) => setValue(value)} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> <InputOTPSlot index={6} /> <InputOTPSlot index={7} /> </InputOTPGroup> </InputOTP> </div> <p className="text-xs text-muted-foreground text-center"> Format: XXXX-XXXX (letters and numbers only) </p> </div>)}API Reference
InputOTP
The root component that manages the OTP input state.
Prop
Type
InputOTPGroup
Groups OTP slots together.
Prop
Type
InputOTPSlot
Individual slot for a single character.
Prop
Type
InputOTPSeparator
Visual separator between groups of slots.
Prop
Type
Features
Smart Input Handling
- Auto-advance: Automatically moves focus to the next slot when typing
- Auto-backspace: Moves focus to previous slot when backspacing
- Paste support: Intelligently handles pasted content
- Pattern validation: Supports RegExp patterns for input validation
Visual Feedback
- Active state: Shows which slot is currently focused
- Completion state: Visual feedback when all slots are filled
- Error state: Can be styled to show validation errors
- Caret animation: Animated cursor in empty focused slots
Accessibility
- Keyboard navigation: Full keyboard support with arrow keys
- Screen readers: Proper ARIA attributes and announcements
- Focus management: Intelligent focus handling between slots
- Copy/paste: Supports clipboard operations
Accessibility
The InputOTP component provides comprehensive accessibility:
- Keyboard Navigation:
- Arrow keys move between slots
- Backspace moves to previous slot and clears
- Delete clears current slot
- Home/End move to first/last slot
- Screen Readers: Proper ARIA attributes and slot announcements
- Focus Management: Visual focus indicators and logical focus flow
- Input Methods: Supports various input methods and IME
Security Note: For sensitive codes like passwords or PINs, consider using the appropriate inputMode and autoComplete attributes for better user experience while maintaining security.
Common Patterns
Phone Number Verification
<InputOTP maxLength={6} pattern="^\\d+$">
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>Two-Factor Authentication
<InputOTP maxLength={6} pattern="^\\d+$">
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>Backup Codes
<InputOTP maxLength={8} pattern="^[A-Z0-9]+$">
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
<InputOTPSlot index={6} />
<InputOTPSlot index={7} />
</InputOTPGroup>
</InputOTP>Styling
The InputOTP components support extensive customization:
/* Custom slot styling */
<InputOTPSlot
index={0}
className="w-12 h-12 text-lg border-2 border-blue-500 rounded-lg"
/>
/* Custom container styling */
<InputOTP
containerClassName="gap-4"
className="justify-center"
maxLength={6}
>
{/* slots */}
</InputOTP>Input
A versatile text input component that supports different types, states, and styling options. Built with accessibility and form integration in mind.
Label
A label component built on Radix UI's Label primitive that provides accessible labeling for form controls. It automatically associates with form elements and supports proper focus management.