OTP input
A segmented one-time-code field. Typing moves forward, Backspace moves back, and a paste fills every box.
vue
<script setup lang="ts">
import SaOtpInput from '@shared/components/common/SaOtpInput.vue'
import { ref } from 'vue'
const code = ref('')
const confirmed = ref('')
function onComplete(value: string): void {
confirmed.value = value
}
</script>
<template>
<div class="max-w-sm space-y-3">
<SaOtpInput v-model="code" @complete="onComplete" />
<p class="text-sm text-muted-foreground">
{{ confirmed ? `Submitted ${confirmed}` : 'Type or paste six digits' }}
</p>
</div>
</template>Usage
v-model is the joined digit string, not an array. Non-digits are ignored on the way in, so a pasted 482-913 becomes 482913.
complete fires the moment every box is filled, whether the last digit was typed or pasted. That is where you submit, rather than asking the reader to press a button they no longer need.
The group is dir="ltr" in every language. A code is read left to right, so the first box stays on the left in a right-to-left layout and ArrowRight still means the next box.
Examples
A different length
length sets the number of boxes. It defaults to 6.
vue
<script setup lang="ts">
import SaOtpInput from '@shared/components/common/SaOtpInput.vue'
import { ref } from 'vue'
const pin = ref('')
</script>
<template>
<div class="max-w-[12rem]">
<SaOtpInput v-model="pin" :length="4" />
</div>
</template>Disabled
vue
<script setup lang="ts">
import SaOtpInput from '@shared/components/common/SaOtpInput.vue'
import { ref } from 'vue'
const used = ref('482913')
</script>
<template>
<div class="max-w-sm">
<SaOtpInput v-model="used" disabled />
</div>
</template>API
Source: src/shared/components/common/SaOtpInput.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | '' | The joined digits. Longer than length is trimmed. |
length | number | 6 | The number of boxes. |
disabled | boolean | false | Dims the boxes and blocks them. |
Emits
| Event | Payload | Description |
|---|---|---|
update:modelValue | value: string | Fires on every keystroke and on paste. |
complete | value: string | Fires when every box is filled. |
Slots
None.
Accessibility
- Each box is named "Digit 1", "Digit 2" and so on.
- Every box carries
inputmode="numeric"andautocomplete="one-time-code", so a phone shows the number pad and can offer the code from an SMS. ArrowLeftandArrowRightmove between boxes.Backspacein an empty box moves back and clears the box it lands on.- Pasting into any box fills from the first, and focus lands on the first box still empty.