ngx-otp-input
v2.0.0
Published
One Time Password input library for Angular (17.2+)
Maintainers
Readme
ngx-otp-input
ngx-otp-input is a standalone, CVA-first Angular component for One-Time Password (OTP) entry.
It combines:
- robust mobile input behavior (
autocomplete="one-time-code", paste handling) - polished boxed UI
- Angular Forms integration
- accessibility defaults (labels, invalid state, live status announcements)
Compatibility
- Angular
>=17.2.0 @angular/formsis required
Installation
npm install ngx-otp-inputBasic usage (Reactive Forms)
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { NgxOtpInputComponent, OtpStatus } from 'ngx-otp-input';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule, NgxOtpInputComponent],
template: `
<form [formGroup]="form">
<ngx-otp-input
formControlName="otp"
[length]="6"
[status]="status"
(otpComplete)="onOtpComplete($event)"
></ngx-otp-input>
</form>
`,
})
export class AppComponent {
status: OtpStatus = 'idle';
form = new FormGroup({
otp: new FormControl('', { nonNullable: true }),
});
onOtpComplete(code: string): void {
// verify code and update this.status
}
}Advanced usage example
<ngx-otp-input
formControlName="otp"
[length]="8"
[autoFocus]="true"
[autoBlur]="false"
[mask]="true"
[charPattern]="/^[A-Za-z0-9]$/"
inputMode="text"
ariaLabel="Enter your 8-character verification code"
[status]="status"
[statusMessages]="{
success: 'Verification successful.',
error: 'Verification failed. Please try again.'
}"
(otpChange)="onOtpChange($event)"
(otpInvalid)="onOtpInvalid($event)"
></ngx-otp-input>API
Inputs
| Input | Type | Default | Description |
| ---------------- | ------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------ |
| length | number | 6 | Number of OTP characters / visual boxes. Minimum effective value is 1. |
| autoFocus | boolean | true | Automatically focuses the input after view init (if enabled and not disabled). |
| autoBlur | boolean | true | Blurs the input when all characters are entered. |
| mask | boolean | false | Displays • in the visual boxes while preserving the actual value. |
| charPattern | RegExp | /^\d$/ | Validates each character individually. |
| inputMode | string | 'numeric' | Keyboard hint for touch devices (numeric, text, etc.). |
| ariaLabel | string | 'One Time Password' | Accessible label for the input group. |
| status | OtpStatus | 'idle' | Status state: 'idle' \| 'success' \| 'error'. |
| statusMessages | OtpStatusMessages | { success: 'Code verified.', error: 'Invalid code.' } | Custom text announced in the live region for success/error states. |
Outputs
| Output | Type | Description |
| ------------- | ------------------------------- | --------------------------------------------------------------------------------- |
| otpChange | EventEmitter<OtpChangeEvent> | Emitted on every valid change with { value, isComplete }. |
| otpComplete | EventEmitter<string> | Emitted once the value reaches length. |
| otpInvalid | EventEmitter<OtpInvalidEvent> | Emitted when characters are rejected (char-rejected) or truncated (too-long). |
Exported types
type OtpStatus = 'idle' | 'success' | 'error';
interface OtpStatusMessages {
success?: string;
error?: string;
}
interface OtpChangeEvent {
value: string;
isComplete: boolean;
}
interface OtpInvalidEvent {
reason: 'too-long' | 'char-rejected';
attemptedValue: string;
acceptedValue: string;
}Public method
NgxOtpInputComponent exposes:
reset(): void
import { Component, ViewChild } from '@angular/core';
import { NgxOtpInputComponent } from 'ngx-otp-input';
export class AppComponent {
@ViewChild(NgxOtpInputComponent) otpInput?: NgxOtpInputComponent;
clearOtp(): void {
this.otpInput?.reset();
}
}Styling
The component exposes stable class names for custom theming:
ngx-otp-input-rootngx-otp-input-nativengx-otp-input-formngx-otp-input-boxngx-otp-input-activengx-otp-input-disabledngx-otp-input-filledngx-otp-input-successngx-otp-input-failedngx-otp-input-status
Example:
ngx-otp-input {
.ngx-otp-input-box {
width: 2.75rem;
height: 2.75rem;
border-radius: 0.75rem;
}
.ngx-otp-input-box.ngx-otp-input-active {
border-color: #6366f1;
box-shadow: 0 0 0 3px rgb(99 102 241 / 20%);
}
}Accessibility notes
- Uses one native input for predictable assistive technology behavior.
- Sets
aria-invalidwhen status iserroror invalid input is detected. - Announces status messages through a polite
aria-liveregion. - Keeps visual OTP boxes hidden from screen readers to avoid duplicate output.
Behavior details
- Input is sanitized on typing and paste.
- Values longer than
lengthare truncated. - Characters not matching
charPatternare ignored. - Arrow keys, backspace, and delete are supported for keyboard editing.
Migration (v1 to v2)
Version 2.x is a breaking release:
- Removed the old
optionsobject API. - Removed direct
otpinput binding. - Moved to explicit inputs and ControlValueAccessor-based forms usage.
- Updated event payloads and status values.
Links
- Demo: https://ngx-otp-input.vercel.app
- Changelog: https://github.com/pkovzz/ngx-otp-input/blob/master/CHANGELOG.md
- Contributing: https://github.com/pkovzz/ngx-otp-input/blob/master/CONTRIBUTING.md
- Code of conduct: https://github.com/pkovzz/ngx-otp-input/blob/master/CODE_OF_CONDUCT.md
- License: https://github.com/pkovzz/ngx-otp-input/blob/master/LICENSE
