/// Inputs
OTPInput
A row of single-character cells for one-time-code / verification entry, with auto-advance, paste distribution, and arrow navigation.
import { OTPInput } from "@protocore/pds";Basics
Six numeric cells. Typing auto-advances, onValueChange reports the concatenated string on every edit, and onComplete fires once when the last cell fills. Controllable via value / defaultValue.
Alphanumeric & masked
Set type="alphanumeric" to accept letters and digits, and mask to obscure each character — the pattern for backup / recovery keys. length sizes the row.
In a Field, with error
Wrap in a Field for a label, hint, and announced error. The error path here triggers once a full-but-wrong code is entered.
We texted a 6-digit code to •••• 4471.
When to use it
Use OTPInput for short, fixed-length codes a user transcribes from a text message, authenticator app, or email — 2FA codes, email confirmations, recovery keys. The segmented cells make length obvious and paste-friendly. For free-form numeric entry (quantities, amounts) use NumberInput; for ordinary text use Input.
Usage
Do
- Give the group an `aria-label` describing the code (e.g. "Two-factor code").
- Match `length` to the real code length so paste fills exactly.
- Use `type="numeric"` for digit-only codes — it sets the numeric `inputMode` for mobile keypads.
- Handle `onComplete` to auto-submit, but still allow editing before the request resolves.
Don't
- Don't use OTPInput for variable-length secrets like passwords — use a masked Input.
- Don't stack more cells than the code has; empty trailing cells can never complete.
- Don't disable paste — users routinely paste codes from another app.
- Don't rely on cell color alone to signal an error; pair with a Field error message.
Accessibility
| Keys | Action |
|---|---|
| 0–9 / A–Z | Fill the focused cell and advance to the next. |
| Backspace | Clear the focused cell, or step back and clear the previous one if empty. |
| Delete | Clear the focused cell in place. |
| Arrow Left / Right | Move between cells without changing values. |
| Home / End | Jump to the first / last cell. |
| Paste | Distribute the pasted characters across cells from the focused one. |
- The cells are wrapped in a `role="group"` labelled by `aria-label`; each cell carries a positional label ("Digit 1 of 6").
- Focusing a cell selects its content so a keystroke replaces the character.
- The first cell advertises `autoComplete="one-time-code"` so iOS / Android can offer the SMS code.
OTPInput props
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | Verification code | Accessible label for the cell group. Default `"Verification code"`. |
className | string | — | |
defaultValue | string | — | Initial value when uncontrolled. |
disabled | boolean | false | Disable every cell. |
length | number | 6 | Number of character cells. Default `6`. |
mask | boolean | false | Obscure entered characters (renders each cell as a password box). |
onComplete | ((value: string) => void) | — | Fires once with the full string when every cell is filled. |
onValueChange | ((value: string) => void) | — | Fires with the full concatenated string whenever any cell changes. |
style | CSSProperties | — | |
type | enum | numeric | Accepted character class: `"numeric"` (0–9, default) or `"alphanumeric"`. |
value | string | — | Controlled value. Pair with `onValueChange`. |