Input GroupLabel
Get Started
Introduction
Components
Accordion
Badge
Button
Card
Checkbox
Command
Dialog
Dropdown Menu
Input
Select
Switch
Table
Tabs
Toast
Tooltip
Forms
Introduction
Validation
Field Arrays

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/client"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/client"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

PropTypeDefaultDescription
maxLengthnumber-The maximum number of characters. Determines how many slots to fill.
valuestring-The controlled value of the OTP input.
defaultValuestring''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.
patternRegExp | 'digits' | 'chars' | 'digits-and-chars''digits'Pattern to validate each character. Use a preset name ('digits', 'chars', 'digits-and-chars') or a RegExp.
disabledbooleanfalseWhether the input is disabled.