Skip to content
Ndop UI

Input OTP primitive

Headless Input OTP: the behavior, keyboard support and ARIA of its pattern, with no styles.

  • @ndop/primitives/input-otp

Import

Primitives ship in the @ndop/primitives package and have no styles. Their state is on data attributes for you to style. The styled Input OTP is built on this one.

import {
  NdInputOtp,
  NdInputOtpGroup,
  NdInputOtpSeparator,
  NdInputOtpInput,
  NdInputOtpSlot,
} from '@ndop/primitives/input-otp';

Accessibility

Keyboard

Keys the component handles.

Key Action
BackspaceDeletes the last character.

Focus

  • Clicking any slot focuses the input, with the caret at the end of the code.
  • Focus stays on the input as the code fills; the completed output lets the app move on.
  • Leaving the input marks a bound field touched.

Right-to-left

The root has dir="ltr": a code's characters read left to right in every language, as phone numbers do, so the slots don't mirror.

API reference

NdInputOtp

[ndInputOtp]from @ndop/primitives/input-otp

Headless one-time code input: one native input[ndInputOtpInput] holds the code, so screen readers, autofill and password managers see a single field, and [ndInputOtpSlot]s draw its characters. Characters allow rejects are dropped, and pasting fills the code from the start. The root owns the value, so it's the ControlValueAccessor that Signal Forms ([formField]), reactive and template-driven forms bind (ADR-0004, amendments 1 and 4).

NdInputOtp binding Type Default
[defaultValue]
Initial value when it isn't bound.
string''
[length]required
The code's length: how many slots it fills.
number—
[allow]
A regular expression one character must match to be kept, or its source. Digits by default.
RegExp/\d/
[inputMode]
The input's inputmode, which picks the on-screen keyboard.
'numeric' | 'text''numeric'
[disabled]
Disables the input. A disabled bound field does too.
booleanfalse
[invalid]
Marks the code invalid when no forms library is bound; with one, the field's state decides.
booleanfalse
[(value)]
The code. Two-way bindable with [(value)].
string''
(completed)
Emits the code when the user completes it.
string—
Data attribute Values
data-complete —
data-disabled —
data-invalid —

NdInputOtpGroup

[ndInputOtpGroup]from @ndop/primitives/input-otp

A group of NdInputOtpSlots, hidden from assistive technology: the input is what it reads.

Manages aria-hidden.

NdInputOtpSeparator

[ndInputOtpSeparator]role separatorfrom @ndop/primitives/input-otp

A visual separator between groups of slots, hidden from assistive technology.

Manages aria-hidden.

NdInputOtpInput

input[ndInputOtpInput]role nativefrom @ndop/primitives/input-otp

The native input of a NdInputOtp, which holds the whole code. Name it with a label or aria-label; style it to sit, invisible, over the slots, so clicking a slot focuses it.

Manages aria-invalid.

NdInputOtpInput binding Type Default
[id]
Element id; also the key a label[ndLabel] uses to find it.
string | undefined—
Data attribute Values
data-invalid —

NdInputOtpSlot

[ndInputOtpSlot]from @ndop/primitives/input-otp

One character of a NdInputOtp's code. Its text is that character; data-active marks where the next one goes, and data-caret an active empty slot, to draw a caret in.

Manages aria-hidden.

NdInputOtpSlot binding Type Default
[index]required
The character's position in the code, from 0.
number—
Data attribute Values
data-active —
data-filled —
data-caret —
data-invalid —