How to Handle iOS SMS Auto-Populate in OTP Inputs with vue3-otp-input

The vue3-otp-input library automatically handles iOS SMS auto-populate by detecting multi-character input events in single-otp-input.vue, synthesizing a clipboard event, and distributing the OTP across all input fields via the parent's handleOnPaste method.

The vue3-otp-input repository provides a specialized Vue 3 component for one-time password entry. Handling iOS SMS auto-populate requires specific architectural workarounds because iOS fills the entire OTP code into a single focused input rather than distributing characters across multiple boxes. This article explains how the library implements seamless iOS auto-fill support through synthetic clipboard events and input field coordination.

How iOS Auto-Populate Works in vue3-otp-input

Component Architecture

The library consists of two coordinated components:

  • vue3-otp-input.vue (parent): Renders multiple <SingleOtpInput> children, manages focus state, concatenates values, and emits on-change and on-complete events.
  • single-otp-input.vue (child): Represents individual <input> elements, normalizes user interactions (typing, pasting, navigation), and forwards events to the parent.

Detecting iOS Auto-Fill Events

When iOS auto-populates an OTP, the OS fills the complete code into the currently focused input. Unlike standard paste operations, this triggers an input event without firing onPaste. The child component detects this scenario in src/components/single-otp-input.vue:

// handleOnChange method in single-otp-input.vue
if (value && value.trim().length > 1) {
  // iOS has auto-filled the whole OTP → synthesize a clipboard event
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  e.clipboardData = { getData: () => value.trim() };
  return emit("on-paste", e as ClipboardEvent);
}
return emit("on-change", value);

Source: [lines 52-63 of single-otp-input.vue](https://github.com/ejirocodes/vue3-otp-input/blob/main/src/components/single-otp-input.vue#L52-L63)

This code creates a synthetic ClipboardEvent by injecting a clipboardData object into the event, allowing the library to treat iOS auto-fill identically to a standard paste operation.

Parent Component Paste Handling

The parent component receives the synthetic event through its handleOnPaste method in src/components/vue3-otp-input.vue:

const handleOnPaste = (event: any) => {
  event.preventDefault();
  const pastedData = event.clipboardData
    .getData("text/plain")
    .slice(0, props.numInputs - activeInput.value)
    .split("");
  
  // Distribute pasted characters across inputs
  combinedWithPastedData.slice(0, props.numInputs).forEach((value, i) => {
    otp.value[i] = value;
  });
  focusInput(combinedWithPastedData.slice(0, props.numInputs).length);
  return checkFilledAllInputs();
};

Source: [lines 112-138 of vue3-otp-input.vue](https://github.com/ejirocodes/vue3-otp-input/blob/main/src/components/vue3-otp-input.vue#L112-L138)

This method splits the received string into individual characters, fills each SingleOtpInput sequentially, updates the combined OTP value, and adjusts focus to the appropriate field.

iOS-Specific Key Handling

Because iOS may suppress certain key events when using the virtual keyboard, the child component includes a user-agent check to bypass normal key-filtering logic:

// handleOnKeyDown in single-otp-input.vue
if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
  emit("on-keydown", event);
  return;
}

Source: [lines 78-84 of single-otp-input.vue](https://github.com/ejirocodes/vue3-otp-input/blob/main/src/components/single-otp-input.vue#L78-L84)

This ensures that OS-filled characters are not inadvertently blocked by the component's standard key validation.

The maxlength Attribute Strategy

Only the last input in the sequence should enforce a single-character limit. To prevent the browser from truncating the iOS auto-filled string, the component conditionally applies :maxlength:

<!-- single-otp-input.vue template -->
<input 
 
  :maxlength="isLastChild ? 1 : undefined" 
 
/>

Source: [lines 49-52 of single-otp-input.vue](https://github.com/ejirocodes/vue3-otp-input/blob/main/src/components/single-otp-input.vue#L49-L52)

This allows the first (focused) input to accept the complete OTP string, which the on-change handler then converts into a distributed paste event.

Implementation Flow

The complete iOS SMS auto-populate handling follows this sequence:

  1. Component Mounting: The parent renders child inputs and optionally auto-focuses the first field via shouldAutoFocus.
  2. SMS Arrival: iOS detects the OTP in an incoming message and presents the auto-fill suggestion.
  3. Auto-Fill Execution: The user taps the suggestion, and iOS injects the complete OTP into the focused input.
  4. Event Interception: The handleOnChange method in single-otp-input.vue detects the multi-character input and synthesizes a ClipboardEvent.
  5. Distribution: The parent component's handleOnPaste receives the event, splits the string, and distributes characters across all inputs.
  6. Completion: Once all fields are filled, the component emits on-complete with the concatenated OTP value.

Code Examples

Basic Usage with Auto-Focus

Enable iOS SMS auto-populate by ensuring the first input is focused and using the appropriate input type:

<script setup lang="ts">
import { ref } from "vue";
import VOtpInput from "vue3-otp-input";

const otpValue = ref("");
const handleComplete = (code: string) => console.log("OTP received:", code);
</script>

<template>
  <v-otp-input
    v-model:value="otpValue"
    :num-inputs="6"
    input-type="tel"
    :should-auto-focus="true"
    @on-complete="handleComplete"
  />
</template>

Key configuration details:

  • input-type="tel" (or "number") signals to iOS that the field expects numeric input, triggering the SMS auto-fill UI.
  • should-auto-focus="true" ensures the first input is focused on mount, which is required for iOS to target the field for auto-population.

Handling Password Inputs

When using input-type="password", the component renders an additional hidden input to suppress browser password managers while maintaining iOS auto-fill compatibility:

<template>
  <v-otp-input
    v-model:value="otpValue"
    input-type="password"
    :num-inputs="4"
    :should-auto-focus="true"
  />
</template>

Implementation detail: The parent component in src/components/vue3-otp-input.vue renders a hidden <input autocomplete="off"> when input-type="password" is specified, preventing password manager interference while allowing the iOS SMS auto-fill mechanism to function normally.

Capturing the Synthetic Paste Event

To detect when iOS has auto-populated the OTP (useful for analytics or logging), listen for the on-paste event:

<script setup lang="ts">
const handlePaste = (event: ClipboardEvent) => {
  const pastedData = event.clipboardData?.getData("text/plain");
  console.log("iOS auto-filled OTP:", pastedData);
  
  // Perform analytics tracking or validation
  trackOtpAutofill(pastedData);
};
</script>

<template>
  <v-otp-input
    v-model:value="otpValue"
    :num-inputs="6"
    input-type="tel"
    @on-paste="handlePaste"
  />
</template>

Because single-otp-input.vue synthesizes a ClipboardEvent when detecting iOS auto-fill, the on-paste handler receives the complete OTP string exactly as it would from a standard paste operation.

Key Source Files

File Purpose Location
src/components/vue3-otp-input.vue Parent component that orchestrates multiple inputs, handles synthetic paste events, and distributes OTP characters across fields. View on GitHub
src/components/single-otp-input.vue Child input component that detects iOS auto-fill via input length analysis and synthesizes clipboard events. View on GitHub
README.md Official documentation covering component props, events, and usage examples. View on GitHub

Summary

  • Automatic iOS detection: The library detects iOS SMS auto-populate by checking if input value length exceeds 1 character in single-otp-input.vue.
  • Synthetic event creation: When iOS auto-fill is detected, the component creates a fake ClipboardEvent with the full OTP string to standardize handling.
  • Cross-input distribution: The parent component vue3-otp-input.vue receives the synthetic paste event and distributes individual characters across all OTP input fields.
  • Agent-specific handling: iOS user agents bypass standard key filtering to ensure OS-filled characters are not blocked by validation logic.
  • Zero-configuration support: iOS SMS auto-populate works out-of-the-box when using should-auto-focus="true" and input-type="tel" or "number".

Frequently Asked Questions

Does vue3-otp-input support iOS SMS auto-fill out of the box?

Yes, iOS SMS auto-populate is supported automatically without additional configuration. The component detects when iOS fills the entire OTP into a single input field and internally redistributes the characters across all inputs. To ensure compatibility, set should-auto-focus="true" so the first input is focused when the SMS arrives, and use input-type="tel" to trigger the iOS numeric keyboard and auto-fill UI.

Why does iOS fill the entire OTP into one input field instead of distributing it?

iOS Safari's auto-fill mechanism is designed to fill form fields completely rather than character-by-character. When the OS detects an OTP SMS, it targets the currently focused input element and injects the entire code string into that single field. The vue3-otp-input library compensates for this behavior by detecting when an input receives more than one character (indicating iOS auto-fill), then treating that multi-character string as a paste event and distributing the characters across the remaining input fields.

How can I detect when iOS auto-populates the OTP for analytics or logging?

You can detect iOS auto-populate by listening to the on-paste event on the v-otp-input component. When iOS fills the OTP, the internal single-otp-input.vue component synthesizes a ClipboardEvent and emits it as on-paste. Your handler will receive the full OTP string via event.clipboardData.getData("text/plain"), allowing you to track the auto-fill occurrence without interfering with the component's internal distribution logic.

What input type should I use to ensure iOS shows the OTP auto-fill suggestion?

Use input-type="tel" (recommended) or input-type="number" to ensure iOS displays the SMS OTP auto-fill suggestion above the keyboard. These input types signal to iOS that the field expects numeric input, which triggers the operating system's one-time password detection and auto-fill UI. When combined with should-auto-focus="true", this configuration ensures the first input is ready to receive the iOS auto-populated value as soon as the SMS arrives.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →