@flbx/vue-otp-input
v1.0.2
Published
Lightweight Vue 3 Two-Factor Authentication input component with full keyboard navigation
Maintainers
Readme
@flbx/vue-otp-input
Lightweight Vue 3 OTP/2FA input component with full keyboard navigation, paste support, and accessibility.
Features
- Lightweight — ~1.4KB gzipped JS + ~0.4KB CSS
- Zero dependencies — Vue 3 only
- TypeScript — Fully typed
- Keyboard navigation — Arrow keys, Backspace, Delete
- Paste support — Paste full OTP codes
- Auto-focus — Moves to next field automatically
- Accessible — ARIA labels included
- Mobile-friendly — Numeric keyboard
Installation
# pnpm
pnpm add @flbx/vue-otp-input
# yarn
yarn add @flbx/vue-otp-input
# npm
npm install @flbx/vue-otp-inputUsage
Basic
<script setup lang="ts">
import { ref } from 'vue'
import { TfaInput } from '@flbx/vue-otp-input'
import '@flbx/vue-otp-input/style.css'
const code = ref('')
</script>
<template>
<TfaInput v-model="code" />
</template>With submit on complete
<script setup lang="ts">
import { ref } from 'vue'
import { TfaInput } from '@flbx/vue-otp-input'
import '@flbx/vue-otp-input/style.css'
const code = ref('')
function submit(value: string) {
console.log('Code:', value)
// call your API here
}
</script>
<template>
<TfaInput
v-model="code"
:length="6"
@complete="submit"
/>
</template>Disabled state
<template>
<TfaInput v-model="code" disabled />
</template>API Reference
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| v-model | string | '' | The OTP code value |
| length | 4 \| 5 \| 6 | 6 | Number of digits |
| disabled | boolean | false | Disable inputs |
Events
| Event | Payload | Description |
|-------|---------|-------------|
| update:modelValue | string | Fires on each digit change |
| complete | string | Fires when all digits are filled |
Styling
Default styles
import '@flbx/vue-otp-input/style.css'CSS Classes
| Class | Description |
|-------|-------------|
| .tfa-input | Container (flexbox) |
| .tfa-input--disabled | When disabled |
| .tfa-input__field | Each input field |
Custom example
.tfa-input {
gap: 1rem;
}
.tfa-input__field {
width: 3rem;
height: 3rem;
font-size: 1.5rem;
border: 2px solid #e2e8f0;
border-radius: 8px;
}
.tfa-input__field:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
}Composable
For custom implementations, use useTfaInput directly.
Options (input)
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| length | number | Yes | Number of digits |
| modelValue | Ref<string> | Yes | Reactive ref for the value |
| onComplete | (value: string) => void | No | Called when all digits filled |
Returns (output)
| Property | Type | Description |
|----------|------|-------------|
| inputRefs | TemplateRefsList | Refs for input elements |
| digits | Ref<string[]> | Array of individual digits (readonly) |
| onInput | Function | Bind to @input |
| onKeydown | Function | Bind to @keydown |
| onFocus | Function | Bind to @focus |
| onPaste | Function | Bind to @paste |
Example
<script setup lang="ts">
import { ref } from 'vue'
import { useTfaInput } from '@flbx/vue-otp-input'
const code = ref('')
const { inputRefs, digits, onInput, onKeydown, onFocus, onPaste } = useTfaInput({
length: 4,
modelValue: code,
onComplete: (value) => console.log('Done:', value),
})
</script>
<template>
<div class="my-custom-otp">
<input
v-for="(digit, index) in digits"
:key="index"
:ref="inputRefs.set"
:value="digit"
type="text"
inputmode="numeric"
maxlength="1"
@input="onInput(index, $event)"
@keydown="onKeydown(index, $event)"
@focus="onFocus"
@paste="onPaste(index, $event)"
/>
</div>
</template>Types
import type { UseTfaInputOptions, UseTfaInputReturn } from '@flbx/vue-otp-input'License
MIT © Florian Beaumont
