Input OTP
Accessible one-time password input with copy-paste support.
bun x bosia@latest add input-otpInput OTP renders a row of character cells that feel like separate boxes but behave like a single input — paste a full code in any cell, backspace to step back, and benefit from mobile SMS autofill via autocomplete="one-time-code".
Preview
Enter your one-time password.
Sub-components
| Component | Purpose |
|---|---|
InputOTP |
Root container with a hidden <input> overlay that owns focus, caret, and selection. |
InputOTPGroup |
Visual grouping for a sequence of slots. |
InputOTPSlot |
Single character cell bound to an index into the current value. |
InputOTPSeparator |
Visual divider between groups (defaults to a minus glyph). |
Props
InputOTP
| Prop | Type | Default |
|---|---|---|
value |
string (bindable) |
"" |
maxlength |
number |
6 |
pattern |
RegExp |
undefined |
disabled |
boolean |
false |
name |
string |
undefined |
id |
string |
undefined |
autocomplete |
string |
"one-time-code" |
inputmode |
"numeric" | "text" | ... |
"numeric" |
onComplete |
(value: string) => void |
undefined |
class |
string |
"" |
containerClass |
string |
"" |
InputOTPSlot
| Prop | Type | Default |
|---|---|---|
index |
number |
— (required) |
class |
string |
"" |
Usage
<script lang="ts">
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
InputOTPSeparator,
} from "$lib/components/ui/input-otp";
let value = $state("");
</script>
<InputOTP
bind:value
maxlength={6}
pattern={/^\d*$/}
onComplete={(code) => console.log("submit", code)}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>Notes
- The real
<input>is absolutely positioned over the slot row withopacity: 0, so it owns focus, caret, paste, and IME while the slot<div>s render the visible characters and an active-cell ring. patternis evaluated on every input — rejected values revert to the last valid string. Defaults to no filter (allow any character).onCompletefires once whenvalue.length === maxlength.- On iOS, SMS autofill suggestions surface because of
autocomplete="one-time-code".