PinInput
A row of single-character boxes for PIN/OTP codes, built on Reka UI's PinInput primitive.
Usage
v-model is an array of strings, one entry per box - typing
auto-advances focus, backspace on an empty box moves back to and clears
the previous one, and pasting a multi-character value fills several
boxes at once:
<script setup lang="ts">
const value = ref<string[]>([])
</script>
<template>
<SPinInput v-model="value" aria-label="Verification code" />
</template>
length sets how many boxes render (5 by default).
OTP autofill
otp sets autocomplete="one-time-code", letting the browser/OS offer
to autofill a code from an incoming SMS, and auto-focuses the first
empty box whenever focus lands anywhere in the group:
<SPinInput v-model="value" otp :length="6" />
Masked
mask renders each box as a password field (dots instead of the typed
character) - a PIN code someone might be entering in public:
<SPinInput v-model="value" mask />
Numeric
type="number" gets the mobile numeric keypad (inputmode="numeric")
and filters out non-digit input - but stores each box as a real
number, not a string, so a leading zero is lost ("0" becomes 0,
and a code like "007" becomes 7). Stick with the default type="text"
whenever leading zeros are significant, which is the common case for a
PIN/OTP code:
<SPinInput v-model="value" type="number" />
Color
The color variant recolors the focus ring only - the resting ring
stays neutral regardless, matching Input:
<SPinInput color="success" />
Forms integration
Wrap it in FormField to get id/name/invalid and
aria-describedby wired up automatically - invalid falls back to
FormField's own state when not set directly on the pin input, and size
does the same.
Custom :ui
To see exactly what you'd be overriding - the current default classes for
every slot and variant - here's PinInput's own theme file:
Props
| Prop | Type | Default |
|---|---|---|
id | string | - |
modelValue | (string | number)[] | - |
defaultValue | (string | number)[] | - |
length | number | 5 |
placeholder | string | - |
mask | boolean | false |
otp | boolean | false |
type | 'text' | 'number' | text |
disabled | boolean | false |
invalid | boolean | false |
size | 'sm' | 'md' | 'lg' | md |
color | 'primary' | 'neutral' | 'secondary' | 'success' | 'danger' | 'info' | 'warning' | primary |
name | string | - |
required | boolean | false |
ui | Partial<Record<'root' | 'input', string | object>> | - |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | (string | number)[] | Fires on every keystroke/paste |
complete | (string | number)[] | Fires once, when every box has a value |