tetra-ui Logotetra ui

OTP Input

A component for entering one-time passwords with individual character slots.

otp-input

Installation

npx shadcn@latest add @tetra-ui/otp-input

Usage

The API mirrors shadcn Input OTPInputOTP maps to OTPInput, InputOTPGroup to OTPInputGroup, and so on.

import {
  OTPInput,
  OTPInputGroup,
  OTPInputSeparator,
  OTPInputSlot,
} from "@/components/ui/otp-input";
<OTPInput maxLength={6} onComplete={(code) => console.log(code)}>
  <OTPInputGroup>
    <OTPInputSlot index={0} />
    <OTPInputSlot index={1} />
    <OTPInputSlot index={2} />
  </OTPInputGroup>
  <OTPInputSeparator />
  <OTPInputGroup>
    <OTPInputSlot index={3} />
    <OTPInputSlot index={4} />
    <OTPInputSlot index={5} />
  </OTPInputGroup>
</OTPInput>

Four digits

A common pattern for PIN codes:

<OTPInput maxLength={4} secureTextEntry onComplete={(code) => console.log(code)}>
  <OTPInputSlot index={0} />
  <OTPInputSlot index={1} />
  <OTPInputSlot index={2} />
  <OTPInputSlot index={3} />
</OTPInput>

Controlled

const [value, setValue] = useState("");

<OTPInput maxLength={6} onChange={setValue} value={value}>
  <OTPInputGroup>
    <OTPInputSlot index={0} />
    <OTPInputSlot index={1} />
    <OTPInputSlot index={2} />
  </OTPInputGroup>
  <OTPInputSeparator />
  <OTPInputGroup>
    <OTPInputSlot index={3} />
    <OTPInputSlot index={4} />
    <OTPInputSlot index={5} />
  </OTPInputGroup>
</OTPInput>

Disabled

<OTPInput disabled maxLength={6}>
  <OTPInputGroup>
    <OTPInputSlot index={0} />
    <OTPInputSlot index={1} />
    <OTPInputSlot index={2} />
  </OTPInputGroup>
  <OTPInputSeparator />
  <OTPInputGroup>
    <OTPInputSlot index={3} />
    <OTPInputSlot index={4} />
    <OTPInputSlot index={5} />
  </OTPInputGroup>
</OTPInput>

Invalid

<OTPInput invalid maxLength={6}>
  <OTPInputGroup>
    <OTPInputSlot index={0} />
    <OTPInputSlot index={1} />
    <OTPInputSlot index={2} />
  </OTPInputGroup>
  <OTPInputSeparator />
  <OTPInputGroup>
    <OTPInputSlot index={3} />
    <OTPInputSlot index={4} />
    <OTPInputSlot index={5} />
  </OTPInputGroup>
</OTPInput>

Pattern

Restrict input to specific character patterns:

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

<OTPInput maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
  <OTPInputGroup>
    <OTPInputSlot index={0} />
    <OTPInputSlot index={1} />
    <OTPInputSlot index={2} />
  </OTPInputGroup>
  <OTPInputSeparator />
  <OTPInputGroup>
    <OTPInputSlot index={3} />
    <OTPInputSlot index={4} />
    <OTPInputSlot index={5} />
  </OTPInputGroup>
</OTPInput>

Form

Use with Field and FieldControl for labels and validation:

import {
  Field,
  FieldControl,
  FieldErrorMessage,
  FieldLabel,
} from "@/components/ui/form";
import {
  OTPInput,
  OTPInputGroup,
  OTPInputSeparator,
  OTPInputSlot,
} from "@/components/ui/otp-input";

<Field errorMessage={error} invalid={!!error}>
  <FieldLabel>Verification code</FieldLabel>
  <FieldControl>
    <OTPInput maxLength={6} onChange={setValue} value={value}>
      <OTPInputGroup>
        <OTPInputSlot index={0} />
        <OTPInputSlot index={1} />
        <OTPInputSlot index={2} />
      </OTPInputGroup>
      <OTPInputSeparator />
      <OTPInputGroup>
        <OTPInputSlot index={3} />
        <OTPInputSlot index={4} />
        <OTPInputSlot index={5} />
      </OTPInputGroup>
    </OTPInput>
  </FieldControl>
  <FieldErrorMessage />
</Field>

On this page