OTP Input
A secure OTP (One-Time Password) input component with auto-focus, paste support, keyboard navigation, resend timer, and visual completion feedback. Perfect for authentication flows and 2FA.
Installation
npx kesp-ui@latest add otp-inputOr specify a custom path: npx kesp-ui@latest add otp-input -p src/components/ui
Preview
InteractiveUsage
import OtpInput from "@/components/ui/otp-input"
function LoginForm() {
const handleComplete = (otp: string) => {
console.log("OTP entered:", otp)
// Verify OTP with backend
verifyOTP(otp)
}
const handleResend = () => {
// Request new OTP from backend
requestNewOTP()
}
return (
<OtpInput
onComplete={handleComplete}
onResend={handleResend}
/>
)
}import OtpInput from "@/components/ui/otp-input"
function TwoFactorAuth() {
const [otp, setOtp] = useState<string>("")
const [error, setError] = useState<string>("")
const handleComplete = async (value: string) => {
try {
const response = await fetch("/api/verify-otp", {
method: "POST",
body: JSON.stringify({ otp: value })
})
if (!response.ok) {
setError("Invalid OTP. Please try again.")
setOtp("") // Clear the input
} else {
// OTP verified successfully
navigateToSecureArea()
}
} catch (err) {
setError("Verification failed. Please try again.")
}
}
return (
<div>
<OtpInput
value={otp}
onChange={setOtp}
onComplete={handleComplete}
onResend={() => requestNewOTP()}
/>
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
</div>
)
}<OtpInput
length={4}
onComplete={(value: string) => verifyShortOTP(value)}
onResend={() => resendOTP()}
/><OtpInput
resendCooldown={60}
onComplete={(value: string) => handleOTP(value)}
onResend={() => {
console.log("Requesting new OTP...")
sendOTP()
}}
/>Key Features
Auto-Focus & Navigation
Automatically focuses the first input on mount. Arrow keys navigate between inputs, Backspace moves back and clears, and typing automatically moves to the next input.
Paste Support
Full clipboard support — paste a complete OTP code and it will automatically distribute across all input boxes, filtering out non-numeric characters.
Resend Timer
Built-in countdown timer prevents spam resends. Shows time remaining in MM:SS format, with a disabled resend button during cooldown period.
Visual Feedback
Active input highlights with a blue ring, filled inputs show blue borders, and completed OTP displays with green borders and a bouncing checkmark. Progress dots below show completion status.
Behavior Details
The OTP input provides an intuitive user experience:
- Keyboard Navigation: Use arrow keys to move between inputs. Backspace deletes current digit or moves back if empty.
- Input Validation: Only numeric digits (0-9) are accepted. All other characters are automatically filtered out.
- Completion Callback: The
onCompletefires immediately when all digits are filled, allowing for instant verification. - Resend Behavior: Clicking resend clears the input, resets the timer, and refocuses the first input box.
For 6-digit OTPs, a separator (–) appears between the 3rd and 4th digits for better readability
Props
| Prop | Type | Default | Description |
|---|---|---|---|
length | 4 | 6 | 6 | Number of OTP digits (4 or 6) |
value | string | undefined | Controlled value for the OTP |
onChange | (value: string) => void | undefined | Called on every digit change |
onComplete | (value: string) => void | undefined | Fired when all digits are entered |
resendCooldown | number | 30 | Cooldown timer in seconds before resend is enabled |
onResend | () => void | undefined | Called when the resend button is clicked |
className | string | "" | Additional CSS classes on the wrapper |
length4 | 66Number of OTP digits (4 or 6)
valuestringundefinedControlled value for the OTP
onChange(value: string) => voidundefinedCalled on every digit change
onComplete(value: string) => voidundefinedFired when all digits are entered
resendCooldownnumber30Cooldown timer in seconds before resend is enabled
onResend() => voidundefinedCalled when the resend button is clicked
classNamestring""Additional CSS classes on the wrapper
Common Patterns
Phone Number Verification
Use a 6-digit OTP for SMS-based phone verification flows.
<OtpInput
length={6}
onComplete={async (otp: string) => {
await verifyPhoneOTP(phoneNumber, otp)
}}
onResend={() => sendSMS(phoneNumber)}
resendCooldown={30}
/>Email Verification
Use a 4 or 6-digit OTP for email verification.
<OtpInput
length={6}
onComplete={(otp: string) => verifyEmail(email, otp)}
onResend={() => {
sendVerificationEmail(email)
toast.success("Verification email sent!")
}}
/>Two-Factor Authentication (2FA)
Implement secure 2FA login with automatic verification.
const [otp, setOtp] = useState<string>("")
const [loading, setLoading] = useState<boolean>(false)
<OtpInput
value={otp}
onChange={setOtp}
onComplete={async (code: string) => {
setLoading(true)
try {
await verify2FA(userId, code)
redirect("/dashboard")
} catch (err) {
setOtp("") // Clear on error
showError("Invalid code")
} finally {
setLoading(false)
}
}}
resendCooldown={60}
/>CLI Reference
npx kesp-ui@latest add otp-inputnpx kesp-ui@latest add otp-input -p src/components/uinpx kesp-ui@latest listnpx kesp-ui --versionnpx kesp-ui --help