# OTP Field

A one-time-password field — a row of character slots that owns focus movement, paste, and completion.

## Installation

```tsx
import { OTPField } from "@makeplane/propel/components/otp-field";
```
## Usage

```tsx
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function BasicDemo() {
  return <OTPField length={6} size="lg" aria-label="Verification code" />;
}
```
## Examples

### Sizes

`size` sets the box size of every slot — `lg`, `xl`, and `2xl`.

```tsx
import { OTPField, type OTPFieldInputSize } from "@makeplane/propel/components/otp-field";

const SIZES: OTPFieldInputSize[] = ["lg", "xl", "2xl"];

export default function SizesDemo() {
  return (
    <div className="flex flex-col items-start gap-4">
      {SIZES.map((size) => (
        <OTPField
          key={size}
          length={6}
          size={size}
          defaultValue="123456"
          aria-label="Verification code"
        />
      ))}
    </div>
  );
}
```

### Grouped

`groups` splits the run with separators — `[3, 3]` renders the 123-456 shape. The counts must sum to `length`.

```tsx
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function GroupedDemo() {
  return <OTPField length={6} size="lg" groups={[3, 3]} aria-label="Verification code" />;
}
```

### Hint

`hint` shows helper text below the slots, e.g. a resend countdown. It's replaced by `error` when set.

```tsx
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function HintDemo() {
  return (
    <OTPField length={6} size="lg" hint="Resend OTP in 15 seconds" aria-label="Verification code" />
  );
}
```

### Invalid

Passing `error` shows the message and flips every slot to the danger state.

```tsx
import { OTPField } from "@makeplane/propel/components/otp-field";

export default function InvalidDemo() {
  return (
    <OTPField
      length={6}
      size="lg"
      defaultValue="12"
      error="Code is invalid"
      aria-label="Verification code"
    />
  );
}
```

### Controlled

`normalizeValue` rewrites accepted characters while rejected ones surface through `onValueInvalid`, fed back as an `error` until the next valid keystroke clears it.

```tsx
import { OTPField } from "@makeplane/propel/components/otp-field";
import * as React from "react";

export default function ControlledDemo() {
  const [error, setError] = React.useState<React.ReactNode>(null);
  return (
    <OTPField
      length={6}
      size="lg"
      validationType="alphanumeric"
      normalizeValue={(value) => value.toUpperCase()}
      onValueChange={() => setError(null)}
      onValueInvalid={() => setError("Only letters and numbers are allowed")}
      error={error}
      aria-label="Recovery code"
    />
  );
}
```
## API Reference

### OTPField

The ready-made one-time-password / verification-code field: a row of `length` character slots.
Drive it with `value`/`defaultValue` + `onValueChange`; the root owns focus movement, paste, and
completion across the slots. Pass `mask` to obscure entered characters, `size` to set the box
size, `hint` for helper text below the slots (e.g. a resend countdown), and `error` to show an
error message instead — `Field.Root invalid` then propagates `data-invalid` to every slot, which
recolors its border (and focus ring) to danger off that state.

Grafts Base UI `OTPField` behavior onto the `elements/otp-field` styled parts (`OTPField` root +
`OTPFieldInput`). Each slot resolves its index from the root context, so the ready-made simply
renders one per slot; `groups` splits the run with separators (e.g. `groups={[3, 3]}` renders
`123-456`).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"lg" \| "xl" \| "2xl"` | — | Box size passed to every slot. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, OTPFieldRootState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `hint` | `ReactNode` | — | Helper text shown below the slots, e.g. a resend countdown. Replaced by `error` when set. |
| `error` | `ReactNode` | — | Error text shown below the slots; its presence flips every slot to the danger state. |
| `groups` | `readonly number[]` | — | Slot counts per visual group, separated by an `OTPFieldSeparator` (e.g. `[3, 3]` renders `123-456`). The counts must sum to `length`; omit for one flat run of slots. |
