How to Handle Password-Type OTP Input for Secure Display with vue3-otp-input
Use the input-type="password" prop to mask each OTP digit and automatically inject a hidden autocomplete blocker that prevents browser autofill while maintaining full event emission and programmatic control.
The vue3-otp-input library provides a dedicated password mode for scenarios requiring secure OTP entry, such as banking authentication or sensitive token verification. By setting the inputType prop to "password", the component masks user input and disables browser autocomplete behaviors that could compromise security. This implementation leverages specific architectural patterns in src/components/vue3-otp-input.vue and src/components/single-otp-input.vue to ensure seamless functionality.
How Password Mode Works in vue3-otp-input
Masking Individual Digits
In src/components/single-otp-input.vue, the component computes the input type through the inputTypeValue computed property (lines 105-109). When the parent passes input-type="password", this computed property returns "password", causing the native HTML <input> element to render with type="password". This triggers the browser's default masking behavior, displaying dots or asterisks instead of the actual characters.
Blocking Browser Autofill
Browser autofill poses a security risk for OTP fields. To prevent this, src/components/vue3-otp-input.vue (lines 21-28) conditionally renders a hidden text input when inputType === 'password'. This element includes autocomplete="off" and style="display:none", which tricks browsers into recognizing the field as non-autofillable. This architectural choice ensures that password managers and browser autocomplete features do not interfere with secure OTP entry.
State Management and Events
Despite the masking, the component maintains full functionality through its reactive state management. The otp ref stores the current values, while activeInput tracks the focused field. When users enter digits, the handleOnChange method triggers changeCodeAtFocus, which updates the state and emits the on-change event with the current value. Once the concatenated length of otp.value equals props.numInputs, the component emits on-complete, delivering the full masked OTP string to the parent component.
Implementation Examples
Basic Password-Type OTP Setup
To implement secure OTP entry, import the component and bind the input-type prop to "password":
<script setup lang="ts">
import { ref } from "vue";
import VOtpInput from "vue3-otp-input";
const otpRef = ref<InstanceType<typeof VOtpInput> | null>(null);
const otpValue = ref("");
const onComplete = (value: string) => {
console.log("OTP Complete:", value);
};
const onChange = (value: string) => {
console.log("Current Value:", value);
};
</script>
<template>
<v-otp-input
ref="otpRef"
v-model:value="otpValue"
:num-inputs="6"
input-type="password"
:should-auto-focus="true"
@on-change="onChange"
@on-complete="onComplete"
placeholder="●"
/>
</template>
This configuration masks each digit, prevents browser autocomplete, and maintains reactive two-way binding through v-model:value.
Programmatic Control
The component exposes methods via defineExpose for runtime manipulation:
<template>
<div>
<v-otp-input
ref="otpRef"
input-type="password"
:num-inputs="6"
/>
<button @click="otpRef?.clearInput()">Clear Input</button>
<button @click="otpRef?.fillInput('123456')">Fill Demo Code</button>
</div>
</template>
The clearInput() method resets all internal state, while fillInput(value) programmatically populates the fields—useful for testing or resending OTP scenarios.
Custom Styling for Masked Inputs
When using password mode, you may want distinct visual treatment for filled versus empty states:
/* Global or scoped stylesheet */
.otp-password-field {
width: 48px;
height: 48px;
font-size: 24px;
text-align: center;
border: 2px solid #e2e8f0;
border-radius: 8px;
transition: all 0.2s;
}
.otp-password-field:focus {
border-color: #3b82f6;
outline: none;
}
.otp-password-field.is-complete {
background-color: #f1f5f9;
border-color: #94a3b8;
}
Apply these classes through the input-classes prop:
<v-otp-input
input-type="password"
input-classes="otp-password-field"
:num-inputs="6"
/>
Key Source Files and Architecture
Understanding the source structure helps debug edge cases and customize behavior:
| File | Purpose | Critical Lines |
|---|---|---|
src/components/vue3-otp-input.vue |
Main wrapper managing OTP state (otp, activeInput), emitting events, and conditionally rendering the hidden autocomplete blocker for password mode. |
Lines 21-28 (hidden input), Lines 45-49 (event emission) |
src/components/single-otp-input.vue |
Individual input component computing inputTypeValue to apply type="password" and handling keyboard navigation, paste events, and focus management. |
Lines 5-11 (template input binding), Lines 105-109 (computed type logic) |
The architecture separates concerns between state management (parent) and input rendering (child), allowing the password mode to function consistently across both single and multi-input configurations.
Summary
- Password masking requires setting
input-type="password", which triggerstype="password"on native inputs via theinputTypeValuecomputed property insingle-otp-input.vue. - Autofill prevention happens automatically when password mode is active;
vue3-otp-input.vueinjects a hidden text field withautocomplete="off"to disable browser password managers. - Full API compatibility is maintained in password mode, including
on-changeandon-completeevents,v-model:valuebinding, and exposed methodsclearInput()andfillInput(). - Custom styling works identically to standard mode via the
input-classesprop, allowing visual differentiation for masked fields.
Frequently Asked Questions
How do I prevent browser autofill on OTP fields when using vue3-otp-input?
The component automatically prevents autofill when you set input-type="password". According to the source code in src/components/vue3-otp-input.vue (lines 21-28), the component conditionally renders a hidden text input with autocomplete="off" and style="display:none" specifically when password mode is detected. This architectural pattern tricks browsers into recognizing the field as non-autofillable without requiring manual autocomplete attributes on visible inputs.
Can I use password mode with numeric-only OTPs?
Yes, password mode works independently of the underlying input restrictions. While the input-type="password" prop controls the visual masking and autofill behavior, you can combine it with validation logic in your parent component to ensure only numeric values are accepted. The single-otp-input.vue component handles the type="password" attribute through its inputTypeValue computed property (lines 105-109), which operates regardless of whether you subsequently validate for numbers, letters, or alphanumeric combinations in your on-change handler.
How do I clear or reset the password OTP input programmatically?
The component exposes clearInput() and fillInput(value) methods via defineExpose in src/components/vue3-otp-input.vue (lines 13-16). To clear the input, obtain a reference to the component using ref<InstanceType<typeof VOtpInput>> and call otpRef.value?.clearInput(). This resets the internal otp reactive array and clears all child input fields. To programmatically fill the OTP (useful for demo purposes or auto-fill scenarios), call otpRef.value?.fillInput('123456'), ensuring the string length matches your configured num-inputs prop.
Does password mode affect the on-complete event payload?
No, password mode does not alter the event payload structure or content. Whether using standard or password mode, the on-complete event emits the complete OTP string exactly as entered by the user. The masking occurs purely at the presentation layer in single-otp-input.vue through the native type="password" attribute, while the actual values are stored in the reactive otp array within vue3-otp-input.vue. When the joined length equals num-inputs, the component emits emit("on-complete", otp.value.join("")) (lines 45-49), delivering the unmasked string to your parent component handler regardless of visual masking.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →