Input OTP
An accessible one-time password input component with copy-paste support.
4
6
#Installation
bunx --bun barefoot add input-otp#Usage
1"use client"23import { createSignal } from "@barefootjs/dom"4import {5 InputOTP,6 InputOTPGroup,7 InputOTPSlot,8 InputOTPSeparator,9} from "@/components/ui/input-otp"1011function InputOTPDemo() {12 const [value, setValue] = createSignal("")1314 return (15 <InputOTP16 maxLength={6}17 pattern="digits-and-chars"18 value={value()}19 onValueChange={setValue}20 >21 <InputOTPGroup>22 <InputOTPSlot index={0} />23 <InputOTPSlot index={1} />24 <InputOTPSlot index={2} />25 </InputOTPGroup>26 <InputOTPSeparator />27 <InputOTPGroup>28 <InputOTPSlot index={3} />29 <InputOTPSlot index={4} />30 <InputOTPSlot index={5} />31 </InputOTPGroup>32 </InputOTP>33 )34}#Examples
#Basic
1import {2 InputOTP,3 InputOTPGroup,4 InputOTPSlot,5} from "@/components/ui/input-otp"67export function InputOTPBasicDemo() {8 return (9 <InputOTP maxLength={4}>10 <InputOTPGroup>11 <InputOTPSlot index={0} />12 <InputOTPSlot index={1} />13 <InputOTPSlot index={2} />14 <InputOTPSlot index={3} />15 </InputOTPGroup>16 </InputOTP>17 )18}#Pattern
Accepts letters and numbers.
1import {2 InputOTP,3 InputOTPGroup,4 InputOTPSlot,5 REGEXP_ONLY_DIGITS_AND_CHARS,6} from "@/components/ui/input-otp"78export function InputOTPPatternDemo() {9 return (10 <div className="space-y-2">11 <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>12 <InputOTPGroup>13 <InputOTPSlot index={0} />14 <InputOTPSlot index={1} />15 <InputOTPSlot index={2} />16 <InputOTPSlot index={3} />17 <InputOTPSlot index={4} />18 <InputOTPSlot index={5} />19 </InputOTPGroup>20 </InputOTP>21 <p className="text-sm text-muted-foreground">22 Accepts letters and numbers.23 </p>24 </div>25 )26}#Form
Verification Code
Enter the 6-digit code sent to your phone. Try 123456 to see success.
1"use client"23import { createSignal, createMemo, onCleanup } from "@barefootjs/dom"4import {5 InputOTP,6 InputOTPGroup,7 InputOTPSlot,8 InputOTPSeparator,9} from "@/components/ui/input-otp"1011export function InputOTPFormDemo() {12 const [value, setValue] = createSignal('')13 const [status, setStatus] = createSignal('idle')14 const [canResend, setCanResend] = createSignal(true)15 const [countdown, setCountdown] = createSignal(0)1617 const isComplete = createMemo(() => value().length === 6)1819 const handleSubmit = () => {20 if (!isComplete()) return21 setStatus('loading')22 setTimeout(() => {23 if (value() === '123456') {24 setStatus('success')25 } else {26 setStatus('error')27 }28 }, 1500)29 }3031 const handleResend = () => {32 if (!canResend()) return33 setCanResend(false)34 setCountdown(30)35 setValue('')36 setStatus('idle')3738 const timer = setInterval(() => {39 setCountdown(prev => {40 if (prev <= 1) {41 clearInterval(timer)42 setCanResend(true)43 return 044 }45 return prev - 146 })47 }, 1000)48 onCleanup(() => clearInterval(timer))49 }5051 return (52 <div className="space-y-4">53 <InputOTP maxLength={6} value={value()} onValueChange={setValue}54 disabled={status() === 'loading' || status() === 'success'}>55 <InputOTPGroup>56 <InputOTPSlot index={0} />57 <InputOTPSlot index={1} />58 <InputOTPSlot index={2} />59 </InputOTPGroup>60 <InputOTPSeparator />61 <InputOTPGroup>62 <InputOTPSlot index={3} />63 <InputOTPSlot index={4} />64 <InputOTPSlot index={5} />65 </InputOTPGroup>66 </InputOTP>67 <div className="flex items-center gap-3">68 <button disabled={!isComplete() || status() === 'loading'}69 onClick={handleSubmit}>70 {status() === 'loading' ? 'Verifying...' : 'Verify'}71 </button>72 <button disabled={!canResend()} onClick={handleResend}>73 {canResend() ? 'Resend code' : `Resend in ${countdown()}s`}74 </button>75 </div>76 {status() === 'success' && <p>Code verified successfully!</p>}77 {status() === 'error' && <p>Invalid code. Please try again.</p>}78 </div>79 )80}#API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| maxLength | number | - | The maximum number of characters. Determines how many slots to fill. |
| value | string | - | The controlled value of the OTP input. |
| defaultValue | string | '' | The default value for uncontrolled mode. |
| onValueChange | (value: string) => void | - | Event handler called when the value changes. |
| onComplete | (value: string) => void | - | Event handler called when all slots are filled. |
| pattern | RegExp | 'digits' | 'chars' | 'digits-and-chars' | 'digits' | Pattern to validate each character. Use a preset name ('digits', 'chars', 'digits-and-chars') or a RegExp. |
| disabled | boolean | false | Whether the input is disabled. |