Get GliaKit

Forms

OTP input

One box per digit for verification codes, with auto-advance, backspace to go back, arrow key movement and paste to fill every slot.

Source
src/components/ui/otp-input.tsx
Figma
OTPInput · state (empty, filled, focus, error)

Usage

Included in the GliaKit boilerplate. Import it where you need it:

tsx
import { OTPInput } from "@/components/ui/otp-input";

When to use

  • Two-factor and email or SMS verification steps.
  • Short numeric PINs where each digit should be visible as it is typed.
  • For codes with letters, use a regular Input: OTPInput strips every non-digit.
  • For passwords, use Input with type="password".

Examples

Error state

Validate in onComplete and pass invalid. Try any code other than 123456.

Disabled

Disable every slot, for example while the code is being checked.

Verification step

A 4-digit code with a verify button that unlocks once every slot is filled.

API reference

OTPInput

Works controlled (value + onChange) or uncontrolled. Does not spread native props.

PropTypeDefaultDescription
lengthnumber6Number of digit slots.
valuestring-Controlled value. Omit to let the component hold its own state.
onChange(value: string) => void-Called with the digits-only code on every change.
onComplete(value: string) => void-Called whenever a change leaves every slot filled.
invalidboolean-Error style on every slot, and aria-invalid on each input.
disabledboolean-Disables every slot.
autoFocusboolean-Focuses the first slot on mount.
classNamestring-Classes for the row of slots.
aria-labelstring"Verification code"Accessible name for the group.

Accessibility

  • The slots sit in a role="group" named by aria-label ("Verification code" by default); each slot is labeled "Digit 1", "Digit 2" and so on.
  • The first slot has autoComplete="one-time-code", so iOS and Android can offer the code from an SMS.
  • inputMode="numeric" opens the number keypad on mobile.
  • Arrow keys move between slots, Backspace clears and steps back, and focusing a slot selects its digit.
  • When invalid, every slot gets aria-invalid. Show the reason in visible text next to the input.

Related