Skip to content

InputOTP ​

An OTP field is a row of boxes for a code that arrived by text or email. The boxes are a picture: underneath there is one hidden input holding the whole code, which is what lets paste, undo and a password manager all work the way they do in any other field.

ts
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "ropav";

max-length decides how long the code is, and each InputOTPSlot takes the index it displays. InputOTPGroup splits the boxes into runs and InputOTPSeparator goes between them.

0
4
variant
template
<InputOTP aria-label="Verification code" default-value="04" :max-length="4">
  <InputOTPGroup>
    <InputOTPSlot :index="0" />
    <InputOTPSlot :index="1" />
    <InputOTPSlot :index="2" />
    <InputOTPSlot :index="3" />
  </InputOTPGroup>
</InputOTP>
0
4
2
input-otp-groups.vue
vue
<script setup lang="ts">
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Label } from "ropav";
</script>

<template>
  <div class="stack">
    <Label>Verification code</Label>
    <InputOTP aria-label="Verification code" default-value="042" :max-length="6">
      <InputOTPGroup>
        <InputOTPSlot :index="0" />
        <InputOTPSlot :index="1" />
        <InputOTPSlot :index="2" />
      </InputOTPGroup>
      <InputOTPSeparator />
      <InputOTPGroup>
        <InputOTPSlot :index="3" />
        <InputOTPSlot :index="4" />
        <InputOTPSlot :index="5" />
      </InputOTPGroup>
    </InputOTP>
  </div>
</template>

<style scoped>
.stack {
  display: flex;
  flex-direction: column;
  gap: calc(var(--rp-spacing) * 2);
}
</style>

What the code may contain ​

pattern is a regular expression tested against the whole value, not one character. Three are exported ready-made — REGEXP_ONLY_DIGITS, REGEXP_ONLY_CHARS and REGEXP_ONLY_DIGITS_AND_CHARS — and input-mode decides which keyboard a phone offers.

auto-complete defaults to one-time-code, which is what lets a phone offer the code straight from the message. Leave it alone unless the code is not one.

paste-transformer rewrites pasted text before it is accepted, for stripping the spaces or the prefix that come with a copied code.

Accessibility ​

  • There is one control, not six, so a screen reader announces one field and the whole value — which is the right unit. Six separate inputs would announce six unlabelled boxes.
  • Name it. The boxes carry no text of their own, so aria-label or a Label is the only thing that says what code is being asked for.
  • push-password-manager-strategy decides how the field gets out of the way of a password manager's badge. The default widens the control rather than letting the badge cover a box.
  • The separator is decoration and announces nothing — the code is read as one string regardless of how the boxes are grouped.

API ​

InputOTP

PropTypeDefaultDescription
ariaDescribedbystring—Ids of the elements that describe the field.
ariaLabelstring—Accessible name, for a field with no visible label.
ariaLabelledbystring—Ids of the elements that name the field.
autoCompletestring"one-time-code"What the browser should offer to fill in.
defaultValuestring—The code the field starts with when nothing is controlling it.
formstring—id of the form to submit with, for a field rendered outside it.
idstring—Lands on the control, which is the field as far as assistive technology is concerned.
inputClassstring—Class for the hidden control. Rarely useful: it is invisible by design.
inputModestring"numeric"Keyboard a touch device should offer.
isDisabledboolean—Whether the field is disabled — no typing, no submission.
isInvalidboolean—Whether the code fails validation. Published to a nested FieldError.
maxLength*number—How many characters the code is. Decides how many slots there are.
namestring—Name submitted with the form.
pasteTransformer((pasted: string) => string)—Rewrites pasted text before it is accepted — to strip spaces or a prefix, say.
patternstring | RegExp—Characters the code may contain, as a regular expression over the whole value.
placeholderstring—Characters shown in the empty slots before anything is typed.
pushPasswordManagerStrategy"increase-width" | "none""increase-width"Whether the control shrinks to clear a password manager's badge.
textAlign"center" | "left" | "right""left"Where the text sits inside the hidden control.
validationDetailsValidationDetails—Which constraint failed, for a caller that wants to branch on the reason.
validationErrorsstring[]—Messages a nested FieldError should show.
valuestring—The code. Set it to take the field over.
variant"primary" | "secondary""primary"Visual variant.

Slots: default.

EventPayload
changestring
completestring
update:valuestring

InputOTPGroup

Slots: default.

InputOTPSeparator

InputOTPSeparator takes nothing but class.

InputOTPSlot

PropTypeDefaultDescription
index*number—Which character of the code this box shows.