npm package: @flbx/vue-otp-input
Description: Lightweight Vue 3 Two-Factor Authentication input component with full keyboard navigation
Author: Florian Beaumont
Latest version: 1.0.2

# @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

```bash
# pnpm
pnpm add @flbx/vue-otp-input

# yarn
yarn add @flbx/vue-otp-input

# npm
npm install @flbx/vue-otp-input
```

## Usage

### Basic

```vue
<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

```vue
<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

```vue
<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

```ts
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

```css
.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

```vue
<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

```ts
import type { UseTfaInputOptions, UseTfaInputReturn } from '@flbx/vue-otp-input'
```

## License

[MIT](./LICENSE) © Florian Beaumont