Input OTP

Enter a one-time code, one character per box.

Installation

Import the class, and @use the entry point in your global styles: without it the component renders unstyled.

import { Component } from '@angular/core';
import { WrInputOtp } from 'ngwr/input-otp';

@Component({ imports: [WrInputOtp] })
export class MyComponent {}

Basic (numeric, 6 cells)

completed fires when every box is filled; paste 123456 to try it.

<wr-input-otp [(value)]="code" (completed)="onCompleted($event)" />

Custom length

length sets the number of boxes.

<wr-input-otp [(value)]="codeShort" length="4" />

Alphanumeric

mode="alphanumeric" accepts letters as well as digits.

<wr-input-otp [(value)]="alphaNumeric" mode="alphanumeric" length="8" />

Masked

mask hides the characters like a password field.

<wr-input-otp [(value)]="secret" mask />

Disabled and read-only

Disabled leaves the tab order and submits nothing; read-only keeps both and refuses typing and paste.

<wr-input-otp disabled [(value)]="code" />
<wr-input-otp readonly [(value)]="code" />

API

NameDescriptionTypeDefault
ariaLabelAccessible name of the whole strip. Falls back to inputOtp.label.string | nullnull
lengthNumber of cells to render. Clamped to [1, 20].number6
modeCharacter set per cell.WrInputOtpMode'numeric'
sizeControl size — shares the --wr-control-* contract. A box carries no size of its own: the host modifier sets --wr-input-otp-size / -font / -radius / -gap and every box reads them from the cascade, so one value sizes the whole strip however many boxes length renders.WrInputOtpSize'md'
maskMask the typed characters like a password.booleanfalse
disabledDisable interaction. Bound automatically from the field's disabled state when used with [formField].booleanfalse
readonlyRefuse edits while every box stays focusable and the code still submits. Bound automatically from the field's readonly state when used with [formField]. Each box is a real text input, so this is the native readonly attribute — arrow keys, Home / End and selection keep working, which is the whole difference from disabled. Paste is cancelled too, since a paste still reaches a read-only input even though typing does not.booleanfalse
placeholderCharacter shown in empty cells.string'•'
valueThe entered code. Bound by [formField], or two-way via [(value)].string''
(completed)Fires on every change made while every cell holds a character — so correcting a digit of an already-complete code emits again. Not latched on the incomplete-to-complete transition: a host auto-submitting on it has to guard its own re-entry, and gets the corrected code for free.string—
(touch)Emitted on blur so a bound field can mark itself touched.void—

CSS variables

Set these on the component's selector or a wrapper, not on :root: the defaults are declared on ngwr/input-otp's own element.

VariableDefaultDeclared on
--wr-input-otp-bgvar(--wr-color-surface).wr-input-otp
--wr-input-otp-bordervar(--wr-border-base).wr-input-otp
--wr-input-otp-cell-font-familyvar(--wr-font-family-mono, monospace).wr-input-otp
--wr-input-otp-cell-font-weightvar(--wr-font-weight-medium).wr-input-otp
--wr-input-otp-cell-line-heightvar(--wr-leading-none).wr-input-otp
--wr-input-otp-colorvar(--wr-color-on-surface).wr-input-otp
--wr-input-otp-focusvar(--wr-color-primary).wr-input-otp
--wr-input-otp-fontvar(--wr-text-lg).wr-input-otp +2 variant overrides
--wr-input-otp-font-familyvar(--wr-font-family-base).wr-input-otp
--wr-input-otp-gapvar(--wr-space-sm).wr-input-otp +2 variant overrides
--wr-input-otp-radiusvar(--wr-control-radius-md).wr-input-otp +2 variant overrides
--wr-input-otp-size2.5rem.wr-input-otp +2 variant overrides

Dependencies

Built onForm

See also