How to Add Custom Separators Between OTP Input Fields in Vue 3

Use the separator prop in vue3-otp-input to inject any HTML markup—characters, emojis, or styled elements—between OTP input fields.

The vue3-otp-input library provides a flexible way to customize the visual spacing between OTP digits. By leveraging the separator prop, you can insert custom characters, SVG icons, or styled HTML elements between each input field without modifying the component's internal logic.

Understanding the Separator Prop Architecture

The separator functionality operates through a parent-child component relationship defined in the source code.

In src/components/vue3-otp-input.vue, the main component accepts the separator prop and forwards it to every SingleOtpInput instance during iteration over the numInputs array【1†L30-L35】.

The child component in src/components/single-otp-input.vue handles the actual rendering logic. It displays the separator only when two conditions are met: the current input is not the last child (!isLastChild) and a non-empty separator string is provided【2†L61-L64】.

Because the component uses v-html to render the separator content, Vue processes the string as raw HTML, enabling complex markup injection.

Implementing Custom OTP Separators

Simple Character Separators

Pass a plain string character to insert basic visual dividers between digits.

<template>
  <v-otp-input
    :num-inputs="4"
    separator="-"
    v-model:value="code"
  />
</template>

<script setup lang="ts">
import { ref } from "vue";
const code = ref("");
</script>

This renders a dash between each of the four input fields.

Emoji and Symbol Separators

The v-html rendering supports Unicode characters and emojis.

<template>
  <v-otp-input
    :num-inputs="6"
    separator="🚀"
    v-model:value="otp"
  />
</template>

<script setup lang="ts">
import { ref } from "vue";
const otp = ref("");
</script>

HTML Component Separators

For styled separators, pass HTML markup directly through the prop.

<template>
  <v-otp-input
    :num-inputs="5"
    :separator="`<span class='otp-separator'>|</span>`"
    v-model:value="pin"
  />
</template>

<style>
.otp-separator {
  color: #888;
  margin: 0 8px;
  font-weight: bold;
}
</style>

Note that styles must be global (not scoped) since the separator renders outside the component's style scope.

Advanced SVG and Component Separators

For complex graphics like SVG icons, inline the markup or compute it in your script.

<script setup lang="ts">
import { ref } from "vue";

const otp = ref("");
const dotSeparator = `<svg width="12" height="12" viewBox="0 0 24 24" style="margin: 0 4px;">
  <circle cx="12" cy="12" r="3" fill="#555"/>
</svg>`;
</script>

<template>
  <v-otp-input
    :num-inputs="4"
    :separator="dotSeparator"
    v-model:value="otp"
  />
</template>

Technical Implementation Details

The separator rendering logic in src/components/single-otp-input.vue uses a conditional template block:

<template v-if="!isLastChild && separator">
  <span v-html="separator"></span>
</template>

This implementation ensures that:

  • No separator appears after the final input field
  • Empty strings do not render unnecessary DOM elements
  • Raw HTML injection occurs safely through Vue's v-html directive

Because v-html disables Vue's reactive bindings on the injected content, avoid using Vue directives (like v-if or @click) inside your separator markup. Use static HTML, CSS, or SVG instead.

Summary

  • The separator prop in vue3-otp-input accepts any HTML string and renders it between input fields using v-html.
  • The component automatically suppresses the separator after the last digit via the isLastChild check in single-otp-input.vue.
  • You can use simple characters, emojis, styled HTML spans, or SVG graphics as separators.
  • Styles for custom HTML separators must be global since the content renders outside scoped style boundaries.

Frequently Asked Questions

Can I use Vue components as separators?

No, you cannot directly pass a Vue component reference to the separator prop because the content renders via v-html, which processes raw HTML strings only. To use a Vue component, extract its rendered HTML markup as a string (using a render function or by inlining the SVG/template markup) and pass that string to the separator prop.

Why isn't my separator showing between the last two inputs?

The single-otp-input.vue component explicitly checks !isLastChild before rendering the separator【2†L61-L64】. This design prevents a trailing separator after the final input field. If you need a separator before the last input, you would need to modify the source code or use CSS pseudo-elements on the container.

Can I apply different styles to individual separators?

All separators receive the same HTML markup string from the separator prop, so you cannot pass different markup to different positions through the prop alone. However, you can use CSS selectors like :nth-child() or :last-of-type on the separator elements (if you assign them a class) to style specific instances differently.

Is the separator prop reactive to changes?

Yes, the separator prop is reactive. Since it is a standard Vue prop passed from vue3-otp-input.vue down to single-otp-input.vue instances, any changes to the separator string in the parent component will immediately update the rendered separators in the DOM through Vue's reactivity system.

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 →