Input OTP

A one-time password input with separate slots, a blinking caret and digits that pop into place as you type.

Preview

Usage

import { z } from 'zod'
import { AutoForm } from '@vitnode/core/components/form/auto-form'
import { AutoFormInputOTP } from '@vitnode/core/components/form/fields/input-otp'
const formSchema = z.object({
  code: z.string().length(6, 'Enter all 6 digits').regex(/^\d+$/, 'Only digits, please'),
})

<AutoForm
  formSchema={formSchema}
  fields={[
    {
      id: 'code',
      component: props => (
        <AutoFormInputOTP
          {...props}
          description="We sent a 6-digit code to your email."
          label="Verify your account"
        />
      ),
    },
  ]}
/>

.length(6) sets the slot count and .regex() becomes the pattern.

One real <input> sits underneath, so paste, password managers and SMS autofill just work.

Groups and patterns

AutoFormInputOTP halves even codes of 6+ characters and keeps the rest in one group. Pass groups to choose.

<AutoFormInputOTP {...props} groups={[2, 2, 2]} label="Backup code" />

Without a schema, set pattern. Common regexes are re-exported:

import { REGEXP_ONLY_DIGITS } from '@vitnode/core/components/ui/input-otp'
<InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
  …
</InputOTP>

REGEXP_ONLY_CHARS and REGEXP_ONLY_DIGITS_AND_CHARS too.

Submit when complete

onComplete fires when every slot is filled, on both components.

<AutoFormInputOTP {...props} onComplete={(code) => verify(code)} />

Wrong code

A wrong code comes from your API. setFormFieldError turns every slot red and shows the message. Try it in the preview.

import { setFormFieldError } from '@vitnode/core/components/ui/form'
<AutoForm
  formSchema={formSchema}
  onSubmit={async (values, form) => {
    const res = await verifyCode(values.code)
    if (!res.ok) setFormFieldError(form, 'code', "That code doesn't match.")
  }}
  fields={[...]}
/>

Accessibility

  • AutoFormInputOTP wires the label, description and error to the input.
  • With InputOTP alone, add an id and a <Label htmlFor>. Link help text and errors with aria-describedby, and set aria-invalid on errors.
  • Slots are hidden from screen readers, so no digit is read twice.
  • Characters animate in, except with reduced motion.

Props

AutoFormInputOTP takes every InputOTP prop except value and children, plus:

Prop

Type

API Reference

input-otp