Components

Pin / OTP Input

Multi-cell input for verification codes, PINs, and fixed-length tokens.

PinInput renders a fixed-length text value as individual cells. It distributes typed or pasted characters, manages focus, supports OTP metadata and masking, and submits one string.

Use Input for unrestricted text.

Anatomy

  • #cell: Replaces one generated cell. Bind inputProps to the focusable input so keyboard, paste, ARIA, and form behavior remain connected.

The component generates one input per character plus a visually hidden native form control. Style generated cells with ui.input.

Usage

Verification code

Paste a six-digit code or enter one digit per cell.

0/6 digits entered

Examples

Paste a one-time code

Set otp on a numeric input. The first cell exposes autocomplete="one-time-code", and pasting fills consecutive cells.

<template>
  <PinInput v-model="code" :length="6" type="number" otp />
</template>

Mask a PIN

Set mask to render password cells while preserving the string model and submitted value.

<template>
  <PinInput v-model="pin" :length="4" type="number" mask aria-label="Security PIN" />
</template>

Restrict characters

Use a regular expression or function for application-specific token rules. Numeric mode is applied before allowedChars.

<template>
  <PinInput
    v-model="inviteCode"
    :length="8"
    :allowed-chars="/^[A-Z0-9]$/"
    aria-label="Invite code"
  />
</template>

Custom cells

#cell receives all required native attributes and handlers in inputProps. Add classes or markup without recreating behavior.

<template>
  <PinInput v-model="code" :length="6">
    <template #cell="{ index, inputProps, isFilled }">
      <label>
        <span class="sr-only">Character {{ index + 1 }}</span>
        <input v-bind="inputProps" :class="{ filled: isFilled }">
      </label>
    </template>
  </PinInput>
</template>

Completion

complete fires after an accepted change fills every cell. It also provides cancelable details for a consistent event shape; cancellation does not roll back the already accepted value.

<template>
  <PinInput v-model="code" :length="6" @complete="verifyCode" />
</template>

Field integration

Inside Field, PinInput inherits id, name, required/disabled/invalid state, and description/error IDs. Incomplete required state appears after blur or an invalid submit.

<template>
  <Field label="Verification code" name="code" required>
    <PinInput v-model="code" :length="6" type="number" otp />
  </Field>
</template>

Cancel a value

value-change fires before the model updates. This can reject a specific sequence without manually restoring the model.

<template>
  <PinInput
    v-model="code"
    @value-change="(value, details) => value.startsWith('0') && details.cancel()"
  />
</template>

Partial positions

Empty positions are preserved in the internal cell draft. Editing or deleting a middle cell never shifts later characters to the left. v-model remains a compact string containing entered characters in position order; it does not encode gaps. A different external model value replaces the cell draft from the first position. complete is emitted only when every cell is filled.

Use the #cell slot's positional values for partial-entry UI, not indexes into the compact model string.

Native Form Reset

A native form reset restores the initial model and clears interaction state. Calling preventDefault() on the reset event preserves the current value. Reset updates v-model without emitting value-change.

API Reference

Model

ModelTypeDefaultDescription
v-modelstring""Canonical value across every cell.

Props

PropTypeDefaultDescription
lengthnumber4Number of cells; clamped to at least one.
type"text" | "number""text"Character mode and input metadata.
maskbooleanfalseRenders password cells.
otpbooleanfalseEnables one-time-code autocomplete on the first cell.
placeholderstring""Placeholder applied to every cell.
allowedCharsRegExp | (character) => booleannon-whitespaceAdditional character filter.
selectOnFocusbooleantrueSelects a cell value when focused.
autoFocusbooleanfalseFocuses the first cell after mount.
idstringfield/generated idFirst cell id. Later cells receive numbered suffixes.
namestringfield nameNative form field name.
requiredbooleanfalseRequires all cells to be filled.
disabledbooleanfalseDisables every cell and form submission.
readOnlybooleanfalsePrevents value changes while retaining focus.
invalidbooleanfalseForces invalid styling and ARIA state.
dir"ltr" | "rtl""ltr"Cell direction and Arrow-key behavior.
ariaLabelstring"Pin input"Group label and per-cell label prefix.
ariaLabelledbystring-Accessible label element id.
ariaDescribedbystringfield IDsAccessible description/error ids.
uiPinInputUi-Classes for structural parts.

Emits

EventPayloadDescription
value-change(value, details)Fires before the canonical value updates. Cancelable.
complete(value, details)Fires after every cell becomes filled.

Slots

SlotPropsDescription
cellindex, value, isFilled, isActive, inputPropsCustom cell. Bind inputProps to an input.

Exposed Methods

MethodDescription
focus()Focuses the first cell.
clear(event?)Clears every cell through value-change.

UI Options

KeyDescription
rootGroup wrapper.
hiddenInputNative form/validity input.
inputEvery generated cell input.

Styling Hooks

UI keyCSS classData attrs
rootakaza-pin-inputdata-akaza-state, data-akaza-disabled, data-akaza-readonly, data-akaza-invalid, data-akaza-focused, data-akaza-filled
hiddenInputakaza-pin-input-hidden-inputnative validity attrs
inputakaza-pin-input-inputdata-akaza-state, data-akaza-index, data-akaza-active, data-akaza-disabled, data-akaza-invalid

Root data-akaza-state is incomplete or complete; cell state is empty or filled. Plain class applies to the root group. Use ui.input for every generated cell.

Keyboard

KeyBehavior
text inputAccepts valid characters and advances focus. Multiple characters distribute forward.
pasteDistributes valid characters from the current cell.
ArrowLeft / ArrowRightMove between cells, respecting dir.
Home / EndFocus first/last cell.
BackspaceClears current cell, or previous cell when current is empty.
DeleteClears current cell.