Cognition

Components

Input OTP

A segmented field for entering short codes one slot at a time. It auto-advances as you type and handles pasting a full code at once.

Preview

Rendered with live Cognition tokens. Click a slot and type, no dark: classes.

Variants

<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
pattern={REGEXP_ONLY_DIGITS_AND_CHARS}

Numeric is the common case for verification codes. Alphanumeric accepts letters and digits for codes that mix both.

States

Default. Empty, at rest.

Active. The focused slot rings (shown statically here).

2
4
6
8

Filled. A complete code.

1
3
5
7

Error. The code was rejected.

<InputOTP disabled>

Active highlights the slot taking input. Filled, error, and disabled read across the whole field.

API

Prop
Type
Default
Description
maxLength
number
required
Number of slots, and the length of the code.
value
string
undefined
The current code when the field is controlled.
onChange
(value: string) => void
undefined
Called with the new code on every keystroke and paste.
pattern
REGEXP_ONLY_DIGITS | REGEXP_ONLY_CHARS | REGEXP_ONLY_DIGITS_AND_CHARS
undefined
Restricts allowed characters. Use the exported regex constants.
disabled
boolean
false
Dims the field and blocks entry.

Slots are composed with InputOTPGroup, InputOTPSlot, and InputOTPSeparator.

Don't and Do

Don't

Don't use an OTP field for general short text. It carries a specific interaction contract, auto-advance and full-code paste, that will confuse people when applied to a name, a code word, or anything that is not entered slot by slot. Use a plain Input for those.

Do
<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>

Use it for verification codes, PIN entry, and any segmented numeric input where slot-by-slot entry is expected.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp";
import { REGEXP_ONLY_DIGITS } from "input-otp";

export function VerifyCode() {
  return (
    <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
      <InputOTPGroup>
        {[0, 1, 2, 3, 4, 5].map((i) => (
          <InputOTPSlot key={i} index={i} />
        ))}
      </InputOTPGroup>
    </InputOTP>
  );
}