Components / Forms

PinInput / OTP

A multi-cell PIN or one-time-code input with paste and complete handling.

Preview

Install

Run in your project
Package manager
npx @getweini/cli@latest add pin-input

Use cases

One-time verification

Collect a six-digit code sent through another channel.

Masked access code

Enter a short numeric secret without displaying each character.

Usage example

Vue
<script setup lang="ts">
import { ref } from 'vue'
import PinInput from './components/ui/pin-input/PinInput.vue'
const code = ref('')
const messages = {
  character: (index: number, total: number) => 'Digit ' + index + ' of ' + total,
}
function verifyCode(value: string) { console.log(value) }
</script>

<template>
  <span id="verification-label">Verification code</span>
  <PinInput
    v-model="code"
    name="verificationCode"
    aria-labelledby="verification-label"
    :length="6"
    :messages="messages"
    @complete="verifyCode"
  />
</template>

More examples

Verification code

Autofill and paste can populate the six cells.

Verification code
<script setup lang="ts">
import { ref } from 'vue'
import PinInput from './components/ui/pin-input/PinInput.vue'
const code = ref('')
const messages = { character: (index: number, total: number) => 'Digit ' + index + ' of ' + total }
function verify() { console.log(code.value) }
</script>
<template>
  <span id="code-label">Verification code</span>
  <PinInput v-model="code" name="code" aria-labelledby="code-label" :length="6" :messages="messages" @complete="verify" />
</template>

Masked access code

Application validation still decides whether the value is acceptable.

Masked access code
<script setup lang="ts">
import { ref } from 'vue'
import PinInput from './components/ui/pin-input/PinInput.vue'
const code = ref('')
const messages = { character: (index: number, total: number) => 'Digit ' + index + ' of ' + total }
</script>
<template>
  <label id="access-label">Access code</label>
  <PinInput v-model="code" aria-labelledby="access-label" :length="4" mask required :messages="messages" />
</template>

Key API

The most useful props, models, slots, events, and methods for this component.

Key API for PinInput / OTP
NameTypeDefault or requirement
v-modelstring''
messages{ character(index, total): string }required
lengthpositive integer6
typenumeric | textnumeric
maskbooleanfalse
autocompletestringone-time-code
id / name / formstringundefined
required / disabled / readonly / invalidbooleanfalse
sizesm | md | lgmd

Name the root group and localize every cell label. Typing advances, paste fills forward, navigation keys move focus, and a hidden named input serializes the complete value for forms.

Implementation notes

  • The complete event fires when interaction fills every cell; verify the value on the server.
  • The hidden form input serializes the whole code. Required completeness needs application validation.

Accessibility and localization

  • Name the whole group and localize the per-cell character labels. Associate errors with aria-describedby.
Weini UIDesign system · Vue component library · Free and public