Vize

PIN Input

One-time-code and PIN entry split across single-character fields with paste distribution.

Package @vizejs/ui/pin-input
Maturity stable
Own the source vize lib pull pin-input
Requires context, controllable-state, id
Aliases otp input, one-time code, verification code, pin code, segmented input
Covers HTML autocomplete one-time-code, Chakra PinInput, Reka UI PinInput

Usage

import { PinInput, PinInputField } from "@vizejs/ui/pin-input";

Or copy the source into your project with vize lib pull pin-input (see Source Distribution).

API

PinInput<Length extends number>

Source: src/families/form/pin-input/pin-input.vue

Props

Prop Type Default Description
length (required) Length required Number of fields. A literal length types the complete characters tuple.
modelValue string undefined Controlled code. undefined selects uncontrolled behavior.
defaultValue string "" Initial uncontrolled code, also restored by form reset.
type PinInputType "numeric" Accepted characters: decimal digits, or letters and digits.
pattern RegExp undefined Single-character pattern that overrides type, for example /[0-9A-F]/.
mask boolean false Render fields as password inputs so entered characters are masked.
otp boolean true One-time-code mode: the first field advertises autocomplete="one-time-code" so browsers can autofill SMS codes.
id string | null undefined Base id; fields use <id>-<index>. null and undefined select a deterministic fallback.
name string undefined Native form field name. A hidden input submits the joined code.
form string undefined Id of a form owner outside the component tree.
placeholder string undefined Placeholder shown in each empty field, for example "○".
disabled boolean false Disable every field and native form submission.
required boolean false Require every field for native constraint validation.
getFieldLabel (index: number, length: number) => string (index, length) => Character ${index + 1} of ${length} Accessible name of each field.
ariaLabel string undefined Accessible name of the group when no label or aria-labelledby supplies one.
ariaLabelledby string undefined Space-separated ids that label the group.
ariaDescribedby string undefined Space-separated ids that describe every field.
ariaErrormessage string undefined Id of the validation error message used while invalid.
ariaInvalid PinInputAriaInvalid false Invalid state announced on every field.

Events

Event Payload Description
update:modelValue [value: string] Fired when the joined code requests a new controlled value.
complete [value: string, characters: PinInputCharacters<Length>] Fired when the last field is filled, with the code and its characters as a typed tuple.

Slots

Slot Slot props Description
default PinInputSlotState Renders one PinInputField per entry in indexes, plus any separators.

Exposed

Member Type Description
value string Entered characters joined, without gaps.
length number Number of fields.
indexes readonly number[] Field indexes to render, [0, 1, ..., length - 1].
complete boolean Whether every field is filled.
disabled boolean Whether the fields are disabled.
state PinInputState Stable state token.
root HTMLDivElement | null Rendered group element.
focus (index?: number) => void Focus a field (default: the first empty one).
setValue (value: string) => boolean Replace the code (filtered and truncated); returns whether it changed.
clear () => boolean Clear every field; returns whether the value changed.

PinInputField

Source: src/families/form/pin-input/pin-input-field.vue

Props

Prop Type Default Description
index (required) number required Zero-based position of this field in the code.
ariaLabel string undefined Accessible name overriding the group's getFieldLabel.

Behavior

Normative state x input -> outcome table for pin-input.vue and pin-input-field.vue (@vizejs/ui/pin-input). The model is the joined code (no gaps); a literal length types the complete characters tuple. Every row is proven by the named test in pin-input.test.ts or pin-input-ssr.test.ts; compile-only assertions live in pin-input.types.test-d.ts.

# State Input Outcome Proven by
PI1 helpers sanitize / write / remove / narrow accepted characters are filtered (NFKC folds full-width), writes clamp to the end and truncate, removals shift left, tuples narrow by length sanitizes, writes, removes, and narrows code characters
PI2 named, seeded render labelled role="group"; fields with deterministic ids, one-time-code autocomplete on the first field, inputmode, per-field labels, hidden joined value renders a labelled group of one-time-code fields with form hooks
PI3 empty typing characters fill the first gap, focus advances, rejects are ignored, complete(value, tuple) fires once, typing over a field replaces it typing fills fields left to right, advances focus, and emits a typed completion
PI4 any paste / SMS autofill pasted or autofilled codes are filtered and distributed across fields; focus lands on the last written field paste and autofill distribute characters across fields
PI5 filled Backspace / Delete / arrows / Home / End Backspace clears the focused character or deletes backwards from an empty field; Delete removes forward; arrows/Home/End move focus; Tab stays native Backspace, Delete, arrows, Home, and End edit and move between fields
PI6 options mask / alphanumeric / pattern / controlled mask renders password fields, type="alphanumeric" uses a text keyboard, pattern overrides accepted characters, controlled values win masking, alphanumeric codes, custom patterns, and controlled values
PI7 in a form submit / reset hidden input submits the joined code; fields are required only until complete; form reset restores defaultValue submits the joined code, requires completion, and restores defaults on reset
PI8 disabled edits every field is natively disabled and edits are ignored disabled codes disable every field and ignore edits
PI9 imperative expose / missing provider focus (first empty field by default), setValue, clear, state; fields outside a PinInput throw VIZE_UI_CONTEXT_MISSING exposes focus, setValue, clear, and state; fields need a PinInput
PI10 SSR / hydration isolated requests byte-identical markup, hydration without diagnostics, and interactive fields renders byte-identical code fields and hydrates without mismatches

Public extension contract

Surface Contract
Parts root (group), field (one native input per character).
Data attributes data-vize-ui, root data-state/data-complete; field data-index/data-filled.
Slots Root default slot receives PinInputSlotState; render a PinInputField per indexes entry.

The subpath is tree-shakable and ships no CSS; those package contracts are pinned by distribution.test.ts, check:size, and check:tree-shaking.