vera logoVera UI
ComponentsForm Components

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-ui
npm install @helgadigitals/vera-ui
yarn add @helgadigitals/vera-ui

Usage

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>