OTP Field
One-time password input coordination across multiple visible cells and one hidden form value.
OTP Field in motion
When to Use
Use OTPField for a short verification code that is entered one character at a time and may be pasted from a message. Use PasswordToggleField for a reusable secret chosen by the person, and Input when the value belongs in one normal text box instead of visually separated cells.
Features
The behavior Atom owns before your product adds appearance.
- Controlled and uncontrolled full value.
- Coordinates rendered input cells and assigns indexes from their render order.
- Optional explicit input indexes.
- Roving tab stop so the field behaves as one logical control.
- Paste distribution across cells.
- Arrow, Backspace, Delete, Home, and End navigation.
- Numeric, alphabetic, alphanumeric, or custom pattern filtering.
- Hidden input for native form submission.
- Optional masking, completion callback, auto-focus, and form submission.
Import
Anatomy
API Reference
Root
Owns the complete code value, filtering, cell registration, focus movement, Field state, completion behavior, and optional hidden form input.
| Prop | Type | Default |
|---|---|---|
children | ReactNode | required |
value | string | - |
defaultValue | string | "" |
onValueChange | (value: string) => void | - |
onComplete | (value: string) => void | - |
length | number | 6 |
type | "numeric" | "alphabetic" | "alphanumeric" | "numeric" |
pattern | RegExp | Derived from type |
mask | boolean | string | false |
name | string | - |
form | string | - |
inputId | string | Generated or inherited from Field |
getInputLabel | (index, length, type) => string | Generated English position label |
autoFocus | boolean | false |
autoSubmit | boolean | false |
disabled | boolean | false |
readOnly | boolean | false |
required | boolean | false |
invalid | boolean | false |
validationBehavior | "inline" | "native" | Field/Form value or "native" |
asChild | boolean | false |
render | RenderProp | - |
| ARIA attribute | Values |
|---|---|
aria-label | Native value, or "Verification code" without an external label |
aria-labelledby | Inherited Field label ID when no direct label is provided |
aria-describedby | Native value or Field descriptions |
aria-invalid | Present when invalid |
| Data attribute | Values |
|---|---|
[data-slot] | "otp-field" |
[data-disabled] | Present when disabled |
[data-readonly] | Present when read-only |
[data-required] | Present when required |
[data-invalid] | Present when invalid |
Input
Renders one visible character cell, joins the roving tab stop, and delegates
typing, paste, deletion, and focus movement to Root.
| Prop | Type | Default |
|---|---|---|
children | ReactNode | - |
index | number | DOM order |
aria-label | string | Generated from index and length |
asChild | boolean | false |
render | RenderProp | - |
| ARIA attribute | Values |
|---|---|
aria-label | "Digit N of length" or "Character N of length" by default |
aria-invalid | Present when the root is invalid |
aria-required | Present when the root is required |
| Data attribute | Values |
|---|---|
[data-slot] | "otp-field-input" |
[data-index] | Zero-based cell index |
[data-filled] | Present when the cell has a value |
[data-disabled] | Present when disabled |
[data-readonly] | Present when read-only |
[data-invalid] | Present when invalid |
Separator
Decorative separator between cells.
| Prop | Type | Default |
|---|---|---|
children | ReactNode | - |
index | number | - |
asChild | boolean | false |
render | RenderProp | - |
| ARIA attribute | Values |
|---|---|
aria-hidden | true |
| Data attribute | Values |
|---|---|
[data-slot] | "otp-field-separator" |
[data-index] | Value from index when provided |
The package exports useOTPFieldContext and its provider for advanced compound
parts. It also exports getOTPFieldPattern, isOTPFieldCharAccepted,
getOTPFieldChars, filterOTPFieldValue, and getOTPFieldDisplayChar for
using the same filtering and display rules outside the rendered field.
Examples
Six Digit Code
Grouped Code
Accessibility
The first visible cell owns required validity and aria-required for the logical OTP value; the
combined named input remains submission-only. A validation attempt is mirrored
across Root and every cell. Inline behavior suppresses the browser bubble.
The root uses role="group" and the visible inputs use roving tabIndex, so
Tab enters the OTP field once. Each input receives a generated position label,
and the separator is hidden from assistive technology. The group does not carry
aria-required, because that attribute is unsupported on role="group". Give the group a clear
label through native aria-label/aria-labelledby or Field. The first visible
cell owns required validity and anchors native browser feedback. The combined
hidden native input is submission-only;
uncontrolled content resets to defaultValue.
Use getInputLabel to localize every generated cell position label. A direct
aria-label on an Input still overrides the generated label for that cell.
| Key | Description |
|---|---|
Tab | Enters or leaves the OTP field as one logical control. |
ArrowRight | Moves to the next cell. |
ArrowLeft | Moves to the previous cell. |
Home | Moves to the first cell. |
End | Moves to the last cell. |
Backspace | Clears the current cell or moves backward when empty. |
Delete | Clears the current cell. |
Paste | Distributes accepted characters across cells. |
Changelog
Unreleased
0.24.0
- Added public Agent Knowledge for component selection, required composition, recurring mistakes, and validation.
0.19.2
- Removed unsupported
aria-requiredfrom therole="group"root while preserving required semantics and native validity on the visible cells.
0.19.0
- Added
getInputLabelfor localizing generated cell position labels.
0.6.16
- Explicitly scrolled inline validation-directed focus into view.
0.6.15
- Exposed inline validation-directed focus through
[data-focus-visible]until blur.
0.6.13
- Added logical-field inline/native validation presentation and synchronized invalid state across Root, visible cells, Field, and Form.
0.6.12
- Moved required validity to the first visible cell and made the combined named value submission-only.
0.5.0
- Removed
ariaLabel/ariaDescribedByin favor of native ARIA and added a required-capable native combined-value input with uncontrolled reset.
0.1.0
- Initial Atom release.