---
title: Input OTP
description: A segmented one-time-code entry field.
---
import Source from './_generated/input-otp.mdx';

A segmented one-time-code field — the kind used for SMS and authenticator codes. A single real input owns typing, paste and the native mobile keyboard; the visible slots mirror the value and blink a caret on the active slot. iOS renders grouped surface-container boxes with a primary ring on the active slot; Android renders outlined slots whose active slot gains a 2px primary edge.

<DemoFrame component="input-otp" />

Because a single real `<input>` backs the slots, the field gets paste-of-a-full-code, the numeric keyboard (`inputmode="numeric"`), and — via `autocomplete="one-time-code"` — the iOS SMS-code suggestion above the keyboard, all for free. The input carries the label and aria; the slots are `aria-hidden` presentation, so a screen reader treats the field as one text input.

## Installation

```bash
npx touchcn add input-otp
```

## Usage

<CodeGroup>

```html Angular
<tcn-input-otp [(value)]="code" ariaLabel="One-time code" (completed)="verify($event)" />
```

```tsx React
import { TcnInputOtp } from '@/components/ui/input-otp';

<TcnInputOtp value={code} onValueChange={setCode} ariaLabel="One-time code" onComplete={verify} />
```

```vue Vue
<script setup lang="ts">
import { TcnInputOtp } from '@/components/ui/input-otp';
</script>

<template>
  <TcnInputOtp v-model="code" aria-label="One-time code" @complete="verify" />
</template>
```

</CodeGroup>

### Grouped slots

`groupSize` inserts a dash separator between groups of slots — e.g. `3` renders a 3 + 3 layout.

<CodeGroup>

```html Angular
<tcn-input-otp [(value)]="code" [groupSize]="3" />
```

```tsx React
<TcnInputOtp value={code} onValueChange={setCode} groupSize={3} />
```

```vue Vue
<TcnInputOtp v-model="code" :group-size="3" />
```

</CodeGroup>

### Alphanumeric codes

By default only digits are accepted. Pass a `pattern` (a regular-expression source matching a single allowed character) and switch the keyboard hint to accept letters.

<CodeGroup>

```html Angular
<tcn-input-otp [(value)]="code" pattern="[a-zA-Z0-9]" inputMode="text" />
```

```tsx React
<TcnInputOtp value={code} onValueChange={setCode} pattern="[a-zA-Z0-9]" inputMode="text" />
```

```vue Vue
<TcnInputOtp v-model="code" pattern="[a-zA-Z0-9]" input-mode="text" />
```

</CodeGroup>

## Props

| Prop           | Type                      | Default     | Description                                              |
| -------------- | ------------------------- | ----------- | ------------------------------------------------------- |
| `value`        | `string` (model, Angular) | `''`        | Two-way bound value (plain string).                     |
| `onValueChange`| `(value: string) => void` | —           | Value callback (React; required).                       |
| `length`       | `number`                  | `6`         | Number of slots / maximum value length.                 |
| `pattern`      | `string`                  | `'[0-9]'`   | Regex source matching a single allowed character.       |
| `groupSize`    | `number`                  | `0`         | Insert a separator between groups of this many slots.   |
| `inputMode`    | `'numeric' \| 'text'`     | `'numeric'` | Virtual-keyboard hint.                                   |
| `ariaLabel`    | `string`                  | —           | Accessible label for the field.                         |
| `disabled`     | `boolean`                 | `false`     | Disable the field.                                       |
| `error`        | `boolean`                 | `false`     | Render the destructive error state.                     |
| `completed` / `onComplete` | `(value: string) => void` | — | Fires once every slot is filled.               |

<Source />
