# OtpInput

A one-time code field shown as equal slots, for email and SMS verification codes. It is one real input with autocomplete one-time-code, not one input per digit, so the platform can fill the code from a message (DECISIONS 2026-10-15). The slots are a visual layer; the input carries the value, the fill and every keyboard behavior. Use Input for free text.

## Usage

### When to use

- Enter a short verification code from an email or SMS, checked digit by digit or in full.
- A code the platform can fill from the message, through the single input's autocomplete one-time-code.

### When not to use

- A longer identifier the user copies in full, such as an API key or a card number. Use Input.
- A numeric quantity the user adjusts. Use NumberInput.
- Free-form text. Use Input.

## Variants

### pattern

numeric accepts digits only (inputmode numeric); alphanumeric also accepts letters.

Values: `numeric`, `alphanumeric`. Default: `numeric`.

### size

Slot size and gap. md by default, lg on touch-first screens, xl for a hero verification step.

Values: `sm`, `md`, `lg`, `xl`. Default: `md`.

## Examples

### Verification code

A six-digit code, verified once complete.

```tsx
const [code, setCode] = React.useState("")
<Field label="Verification code">
  <FieldControl>
    <OtpInput length={6} value={code} onValueChange={setCode} />
  </FieldControl>
</Field>
```

### Rejected code

```tsx
<OtpInput length={6} defaultValue="123456" invalid />
```

### Four digits, touch-first

```tsx
<OtpInput length={4} size="xl" />
```

## Do and don't

### Do

- Use inside Field so the label and error are linked, and set invalid when the server rejects the code.
- Verify in onComplete, and keep the field enabled so the user can correct a rejected code.
- Use lg or xl on touch-first screens; each slot is then 44px or larger.

### Don't

- Do not build one input per digit; the platform fill from a verification message needs the single input.
- Do not use for identifiers the user copies in full, such as an API key; use Input.
- Do not autofocus on pages where the user must read instructions first.

## Props

| Name | Type | Values | Default | Description |
| --- | --- | --- | --- | --- |
| `length` | `number` |  | `6` | Number of slots. The most common codes are 4 and 6 digits. |
| `pattern` | `enum` | `numeric`, `alphanumeric` | `numeric` | numeric accepts digits only (inputmode numeric); alphanumeric also accepts letters. |
| `value` | `string` |  |  | Controlled code value. Characters outside the pattern and beyond length are filtered. |
| `defaultValue` | `string` |  |  | Initial code for the uncontrolled input. |
| `onValueChange` | `function` |  |  | Called with the filtered code on every change. |
| `onComplete` | `function` |  |  | Called once each time the code reaches full length while the user types, so verification can start. |
| `size` | `enum` | `sm`, `md`, `lg`, `xl` | `md` | Slot size and gap. md by default, lg on touch-first screens, xl for a hero verification step. |
| `disabled` | `boolean` |  | `false` | Disables the input; the slots take the disabled fill and text. |
| `invalid` | `boolean` |  | `false` | Every slot takes the danger border, the input sets aria-invalid. |
| `required` | `boolean` |  | `false` | Standard required attribute on the input. |
| `name` | `string` |  |  | Form field name, submitted by the surrounding form. |

### Slots

| Name | Required | Description |
| --- | --- | --- |
| `value` | no | Controlled or uncontrolled code value, filtered to the pattern and length. |

## Anatomy

| Part | Description |
| --- | --- |
| `root` | The strip: a relative flex row that carries the focus ring (has-input-focus-visible) and the size gap. |
| `slot` | One square code cell, sized by size-element so touch sizes apply. Shows the character, or an accent caret bar when it is the next slot to fill while focused. aria-hidden. |
| `caret` | The accent bar in the next slot to fill while focused, in place of the transparent input's caret. |
| `input` | The single text input covering the strip: transparent text and caret over the slots, autocomplete one-time-code, inputmode numeric or text. Carries the value, name, disabled, required and aria-invalid. |

## Accessibility

Role: `textbox`

| Keys | Action |
| --- | --- |
| `0-9 / A-Z` | Fill the next empty slot. Characters outside the pattern are dropped. |
| `Backspace` | Delete the last entered character. |
| `Left / Right` | Move the text caret inside the code, native input behavior. |
| `Paste` | Paste the code; characters outside the pattern are dropped, the rest fill from the start. |

- The slots are aria-hidden; screen readers announce one textbox with the whole code, not one region per digit.
- The platform fill from a verification SMS works because the browser sees a single input with autocomplete one-time-code.
- Use inside Field so the label and error are linked. aria-invalid is set on the input when invalid is true.

## Install

```bash
npx werewolf-ui add otp-input
```

Run `npx werewolf-ui init` once in your app before adding components. Init sets up tokens, their CSS import and agent rules. Add installs this component and its dependencies while preserving your theme. See [Installation](https://werewolf-ui.vercel.app/docs/installation.md) for requirements and the alternative registry setup.
