# Input OTP

InputOtp,
InputOtpGroup,
InputOtpSeparator,
InputOtpSlot,
REGEXP_ONLY_DIGITS_AND_CHARS,
} from "@/components/starwind/input-otp";

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSlot,
} from "@/components/starwind/input-otp";
---

<InputOtp aria-label="Verification code" maxLength={6}>
  <InputOtpGroup>
    <InputOtpSlot index={0} />
    <InputOtpSlot index={1} />
    <InputOtpSlot index={2} />
    <InputOtpSlot index={3} />
    <InputOtpSlot index={4} />
    <InputOtpSlot index={5} />
  </InputOtpGroup>
</InputOtp>
```
  </div>
  <div slot="react">
```tsx
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSlot,
} from "@/components/starwind/input-otp";

export function Example() {
  return (
    <InputOtp aria-label="Verification code" maxLength={6}>
      <InputOtpGroup>
        <InputOtpSlot index={0} />
        <InputOtpSlot index={1} />
        <InputOtpSlot index={2} />
        <InputOtpSlot index={3} />
        <InputOtpSlot index={4} />
        <InputOtpSlot index={5} />
      </InputOtpGroup>
    </InputOtp>
  );
}
```
  </div>
</FrameworkCodeSwitcher>

> **Info:** Input OTP now delegates keyboard navigation, paste distribution, pattern filtering, hidden form
input synchronization, and reset behavior to the Runtime. Give the root an accessible name
because its visible slots share one hidden input.

## Installation

```bash
npx starwind@latest add input-otp
```

## Usage

### General Notes

The Input OTP component provides a user-friendly way to input one-time passwords and verification codes. It supports keyboard navigation, paste handling, and pattern validation.

The essential components are `InputOtp`, `InputOtpGroup`, and `InputOtpSlot`. The `InputOtpSeparator` provides visual separation between groups of slots.

### With Separator

Use `InputOtpSeparator` to visually group slots, commonly used for phone verification codes or formatted inputs.

```astro
---
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSeparator,
  InputOtpSlot,
} from "@/components/starwind/input-otp";
---

<InputOtp aria-label="Verification code with separator" maxLength={6}>
  <InputOtpGroup>
    <InputOtpSlot index={0} />
    <InputOtpSlot index={1} />
    <InputOtpSlot index={2} />
  </InputOtpGroup>
  <InputOtpSeparator />
  <InputOtpGroup>
    <InputOtpSlot index={3} />
    <InputOtpSlot index={4} />
    <InputOtpSlot index={5} />
  </InputOtpGroup>
</InputOtp>
```

> **Info:** Name the `InputOtp` root rather than its individual slots; Runtime exposes the shared name on the
hidden input.

### Alphanumeric Pattern

Use the `pattern` prop with `REGEXP_ONLY_DIGITS_AND_CHARS` to allow both letters and numbers.

```astro
---
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSlot,
  REGEXP_ONLY_DIGITS_AND_CHARS,
} from "@/components/starwind/input-otp";
---

<InputOtp
  aria-label="Alphanumeric verification code"
  maxLength={6}
  pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
>
  <InputOtpGroup>
    <InputOtpSlot index={0} />
    <InputOtpSlot index={1} />
    <InputOtpSlot index={2} />
    <InputOtpSlot index={3} />
    <InputOtpSlot index={4} />
    <InputOtpSlot index={5} />
  </InputOtpGroup>
</InputOtp>
```

> **Info:** Keep an explicit accessible name when the pattern changes what characters the shared input
accepts.

### Sizes

The `InputOtpSlot` component supports three sizes: `sm`, `md` (default), and `lg`.

```astro
---
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSlot,
} from "@/components/starwind/input-otp";
---

<div class="flex flex-col gap-6">
  <div class="space-y-2">
    <p class="text-muted-foreground text-sm">Small</p>
    <InputOtp aria-label="Small verification code" maxLength={4}>
      <InputOtpGroup>
        <InputOtpSlot index={0} size="sm" />
        <InputOtpSlot index={1} size="sm" />
        <InputOtpSlot index={2} size="sm" />
        <InputOtpSlot index={3} size="sm" />
      </InputOtpGroup>
    </InputOtp>
  </div>
  <div class="space-y-2">
    <p class="text-muted-foreground text-sm">Medium (default)</p>
    <InputOtp aria-label="Medium verification code" maxLength={4}>
      <InputOtpGroup>
        <InputOtpSlot index={0} size="md" />
        <InputOtpSlot index={1} size="md" />
        <InputOtpSlot index={2} size="md" />
        <InputOtpSlot index={3} size="md" />
      </InputOtpGroup>
    </InputOtp>
  </div>
  <div class="space-y-2">
    <p class="text-muted-foreground text-sm">Large</p>
    <InputOtp aria-label="Large verification code" maxLength={4}>
      <InputOtpGroup>
        <InputOtpSlot index={0} size="lg" />
        <InputOtpSlot index={1} size="lg" />
        <InputOtpSlot index={2} size="lg" />
        <InputOtpSlot index={3} size="lg" />
      </InputOtpGroup>
    </InputOtp>
  </div>
</div>
```

> **Info:** Size is visual, so each preview uses a distinct accessible name instead of relying on the nearby
size caption.

### Disabled

Use the `disabled` prop to prevent user interaction.

```astro
---
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSlot,
} from "@/components/starwind/input-otp";
---

<InputOtp aria-label="Verification code" maxLength={6} disabled>
  <InputOtpGroup>
    <InputOtpSlot index={0} />
    <InputOtpSlot index={1} />
    <InputOtpSlot index={2} />
    <InputOtpSlot index={3} />
    <InputOtpSlot index={4} />
    <InputOtpSlot index={5} />
  </InputOtpGroup>
</InputOtp>
```

> **Info:** Disabled controls still need an accessible name so assistive technology can identify them.

### Form Submission

The component integrates with native HTML forms using the `name` prop. The hidden input stores the complete OTP value.

```astro
---
import { Button } from "@/components/starwind/button";
import {
  InputOtp,
  InputOtpGroup,
  InputOtpSlot,
} from "@/components/starwind/input-otp";
import { Label } from "@/components/starwind/label";
---

<form id="otp-demo-form" class="space-y-4">
  <div class="space-y-2">
    <Label for="otp-form">Verification Code</Label>
    <InputOtp id="otp-form" name="otp" maxLength={6} required>
      <InputOtpGroup>
        <InputOtpSlot index={0} />
        <InputOtpSlot index={1} />
        <InputOtpSlot index={2} />
        <InputOtpSlot index={3} />
        <InputOtpSlot index={4} />
        <InputOtpSlot index={5} />
      </InputOtpGroup>
    </InputOtp>
  </div>
  <Button type="submit">Submit Code</Button>
</form>
<div id="form-result" class="bg-muted mt-4 hidden rounded-md p-4 font-mono text-sm"></div>

<script>
  const form = document.getElementById("otp-demo-form") as HTMLFormElement;
  const resultDiv = document.getElementById("form-result");

  if (form && resultDiv) {
    form.addEventListener("submit", (e) => {
      e.preventDefault();
      const formData = new FormData(form);
      const otp = formData.get("otp");

      resultDiv.textContent = `Submitted OTP: ${otp}`;
      resultDiv.classList.remove("hidden");
    });
  }
</script>
```

### Value changes

Listen on the Input OTP root for `starwind:value-change`. Its detail includes `value`,
`previousValue`, `reason`, and `inputOtpId`; the event is cancelable before Runtime commits the
change.

```astro
<InputOtp id="verification-code" aria-label="Verification code" maxLength={6}>
  <InputOtpGroup>
    <InputOtpSlot index={0} />
    <InputOtpSlot index={1} />
    <InputOtpSlot index={2} />
    <InputOtpSlot index={3} />
    <InputOtpSlot index={4} />
    <InputOtpSlot index={5} />
  </InputOtpGroup>
</InputOtp>

<script>
  const root = document.querySelector<HTMLElement>("#verification-code");
  root?.addEventListener("starwind:value-change", (event) => {
    const { value, reason } = (
      event as CustomEvent<{ value: string; reason: string }>
    ).detail;
    console.log(value, reason);
  });
</script>
```

> **Info:** Use the shared Runtime value-change contract for new integrations. The older
`starwind-input-otp:change` event remains a compatibility event with less detail.

## Framework Availability
Input Otp is available for Astro and React.
Astro available; React available
| Framework | Status | Notes |
| --- | --- | --- |
| Astro | available | - |
| React | available | - |
## API Reference
### InputOtp
- Inherits div attributes. Omits `defaultValue`, `id`, `onChange`, `pattern`, and `value`.

### InputOtpGroup
- Inherits div attributes.

### InputOtpSlot
| Prop | Type | Required | Default | Kind | Description |
| --- | --- | --- | --- | --- | --- |
| `size` | `"sm" \| "md" \| "lg"` | No | `"md"` | Styled variant | Selects the component's visual size. |
- Inherits div attributes.

### InputOtpSeparator
- Inherits div attributes.
### Primitive And Runtime API
Behavior, state, events, form participation, and imperative methods are documented in the lower-level references.
- Primitive: [Input Otp Primitive](/docs/primitives/input-otp/)
- Runtime factory: [`createInputOtp`](/docs/runtime/#create-input-otp) from `@starwind-ui/runtime/input-otp`

## Changelog

### v2.0.0

- Rebuilt Input OTP on Starwind Runtime for value state, form participation, paste handling, and value-change events.
- See the [Input OTP Primitive](/docs/primitives/input-otp/) for the underlying unstyled anatomy and behavior API.

### v1.0.3 

- Refactor tailwind variants functions into separate `variants.ts` file

### v1.0.1

- Auto focus hidden input on click to automatically open keyboards on mobile devices
- Update component to always focus the next empty index on click

### v1.0.0

- Initial release with starwind v1.15.0