kesp-ui
npm ↗
Component2FA Ready

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-input

Or specify a custom path: npx kesp-ui@latest add otp-input -p src/components/ui

Preview

Interactive
6-Digit OTP (Default)
Enter 6-digit OTP
Resend in 0:30
4-Digit OTP
Enter 4-digit OTP
Resend in 0:30
Controlled Mode
Enter 6-digit OTP
Resend in 0:30
Custom Cooldown (10 seconds)
Enter 6-digit OTP
Resend in 0:10

Usage

Basic Usage
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}
    />
  )
}
Controlled Mode with Validation
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>
  )
}
4-Digit OTP
<OtpInput
  length={4}
  onComplete={(value: string) => verifyShortOTP(value)}
  onResend={() => resendOTP()}
/>
Custom Cooldown Timer
<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 onComplete fires 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

length4 | 6
Default:6

Number of OTP digits (4 or 6)

valuestring
Default:undefined

Controlled value for the OTP

onChange(value: string) => void
Default:undefined

Called on every digit change

onComplete(value: string) => void
Default:undefined

Fired when all digits are entered

resendCooldownnumber
Default:30

Cooldown timer in seconds before resend is enabled

onResend() => void
Default:undefined

Called when the resend button is clicked

classNamestring
Default:""

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

Install component
npx kesp-ui@latest add otp-input
Custom path
npx kesp-ui@latest add otp-input -p src/components/ui
List components
npx kesp-ui@latest list
Check version
npx kesp-ui --version
Help
npx kesp-ui --help