# Input OTP

- Page: <https://kznlabs.github.io/labsUI/components/input-otp>

## Input OTP (`input-otp`, component)

One-time code in separate slots, backed by one real input: paste and SMS autofill work, screen readers hear one field. Grouped slots, numeric or alphanumeric, characters spring in, onComplete to submit. verified turns each character into a check, then merges them into one mark.

- Install: `npx @kznlabs/labsui add input-otp`
- Docs: <https://kznlabs.github.io/labsUI/components/input-otp>
- Brings: field, label, use-controllable-state, utils
- npm: clsx@^2.1.1, lucide-react@1.63.0, motion@14.7.1, tailwind-merge@^3.7.0

### API

- `<InputOTP>` (+ <input> props): One-time code: a single real input (paste, SMS autofill via autocomplete="one-time-code",
one label for screen readers) laid transparently over the slots it draws. Alphanumeric is uppercased.
  - `charset?: "numeric" | "alphanumeric"` = `"numeric"`: Allowed characters; others are dropped as typed or pasted.
  - `defaultValue?: string` = `""`: Code at first render (uncontrolled).
  - `groups?: number[]`: Slot counts per visual group, e.g. `[3, 3]`; a dash sits between groups.
  - `length?: number` = `6`: Number of slots.
  - `onComplete?: (value: string) => void`: Fires once every slot is filled, e.g. to submit.
  - `onValueChange?: (value: string) => void`: Called with the code on every change.
  - `value?: string`: Controlled code; pair with `onValueChange`.
  - `verified?: boolean` = `false`: The code was accepted: each character slides up into a check, then the slots merge into one
mark with `verifiedLabel`. The input turns read-only; set it back to false to edit again.
  - `verifiedLabel?: ReactNode` = `"Verified"`: Shown beside the merged mark and announced to screen readers.

### Usage

```tsx
<InputOTP groups={[3, 3]} value={code} onValueChange={setCode} onComplete={verify} verified={ok} />
```

### Example: two-factor.tsx

```tsx
"use client";

import { useState } from "react";
import { Field } from "@/components/ui/field";
import { InputOTP } from "@/components/ui/input-otp";

export function TwoFactor({ verify }: { verify: (code: string) => Promise<boolean> }) {
  const [code, setCode] = useState("");
  const [error, setError] = useState<string>();
  const [verified, setVerified] = useState(false);
  return (
    <Field label="Verification code" error={error}>
      <InputOTP
        name="code"
        groups={[3, 3]}
        value={code}
        onValueChange={(v) => {
          setCode(v);
          setError(undefined);
        }}
        verified={verified}
        onComplete={async (v) => {
          if (await verify(v)) setVerified(true);
          else setError("That code is not right.");
        }}
      />
    </Field>
  );
}
```
