PinInput

2 parts

A segmented input for short codes — OTP / one-time passwords, 2FA tokens, or PINs split across one box per character. The interactive root: it owns the value as a per-cell string[] (controlled via v-model / update:modelValue or uncontrolled via defaultValue), sizes the field to length, enforces the type ('text' | 'number') and mask, and provides context to each PinInputInput. Emits complete once every cell is filled. Use it for verification codes where each character gets its own cell with auto-advance, arrow-key navigation, and clipboard paste spreading across cells. Native form support: pass name (and optionally required / id) to render a visually-hidden form control holding the joined value, so the field submits with its owning <form> and participates in native required validation.

Demo

Loading demo…

Anatomy

Import the parts and compose them. Each part forwards attributes to its underlying element.

vue
import {
  PinInputInput,
  PinInputRoot,
} from '@robonen/primitives/forms/pin-input';

<PinInputRoot>
  <PinInputInput />
</PinInputRoot>

API Reference

PinInputInput

Input

A single cell of the pin input, identified by its zero-based `index`. Renders one masked-or-plain character box that reads/writes its slot of the root's value and handles typing (auto-advancing to the next cell), Backspace/Delete, arrow/Home/End navigation, and paste (spreading text across cells). Render one per character, with `index` from `0` to `length - 1`. Polymorphic via `as` (defaults to a native `<input>`); `as="template"` merges onto a single child. A per-cell `disabled` prop is honored on top of the root-level `disabled` and is skipped by arrow navigation.

PropTypeDescription
indexnumber
disabled?booleanDisable this individual cell (merged with the root-level disabled).

PinInputRoot

Root

A segmented input for short codes — OTP / one-time passwords, 2FA tokens, or PINs split across one box per character. The interactive root: it owns the value as a per-cell `string[]` (controlled via `v-model` / `update:modelValue` or uncontrolled via `defaultValue`), sizes the field to `length`, enforces the `type` ('text' | 'number') and `mask`, and provides context to each `PinInputInput`. Emits `complete` once every cell is filled. Use it for verification codes where each character gets its own cell with auto-advance, arrow-key navigation, and clipboard paste spreading across cells. Native form support: pass `name` (and optionally `required` / `id`) to render a visually-hidden form control holding the joined value, so the field submits with its owning `<form>` and participates in native `required` validation.

PropTypeDescription
defaultValue?string[]
length?number
mask?boolean
otp?boolean
type?'text' | 'number'
disabled?boolean
placeholder?string
dir?DirectionReading direction. Affects arrow-key navigation (in rtl, ArrowRight moves to the previous cell). Falls back to the active ConfigProvider dir, then ltr.
name?stringName submitted with the owning form (enables the hidden form control).
required?booleanMirror the required constraint so native form validation fires.
id?stringId forwarded to the hidden form control for label association.
modelValue?string[]Two-way bound value (v-model).
Emits
EventPayload
complete[value: string]
update:modelValue[value: string[]]