A customizable, standalone OTP input component for Angular 14+ with full RTL support, masking, paste handling, autoβsubmit, and keyboard navigation.
π If you like this project, please consider giving it a β on GitHub to support the development!
- Features
- Installation
- Compatibility
- Quick Start
- API
- Styling & Theming
- Mobile & Accessibility
- Best Practices
- Live Demo
- Development
- License
- Contributing
- FAQ
- β RTL & LTR Support
- β ControlValueAccessor
- β Auto Submit
- β Auto Blur
- β Input Masking
- β Paste Handling
- β Keyboard Navigation
- β Accessibility Ready
- β Full Custom Styling
npm install ngx-otp-inputsRequires Angular 14+ with Standalone Component support.
- Angular 14+
- Works with Reactive Forms & Template-driven Forms
- SSR-friendly
import { NgxOtpInputsComponent } from "ngx-otp-inputs"; @Component({ standalone: true, imports: [NgxOtpInputsComponent], template: `<lib-ngx-otp-inputs [length]="6" [maskInput]="false" [autoSubmit]="true" [direction]="'ltr'" (completed)="onCompleted($event)" (changed)="onChanged($event)" />`, }) export class MyComponent { onCompleted(code: string) { console.log("Completed:", code); } onChanged(code: string) { console.log("Changed:", code); } }import { Component } from "@angular/core"; import { FormControl, FormGroup, Validators, ReactiveFormsModule } from "@angular/forms"; import { NgxOtpInputsComponent } from "ngx-otp-inputs"; @Component({ standalone: true, imports: [ReactiveFormsModule, NgxOtpInputsComponent], template: ` <form [formGroup]="form" (ngSubmit)="submit()"> <lib-ngx-otp-inputs formControlName="otp" [length]="6" [inputType]="'number'" [inputMode]="'numeric'" [autoSubmit]="true" (completed)="onCompleted($event)"> </lib-ngx-otp-inputs> <button type="submit" [disabled]="form.invalid">Submit</button> <div *ngIf="form.invalid && form.touched" class="error">Enter 6 digits</div> </form> `, }) export class ExampleReactive { form = new FormGroup({ otp: new FormControl("", [Validators.required]), }); onCompleted(code: string) { console.log(code); } submit() { console.log(this.form.value); } }<lib-ngx-otp-inputs [(ngModel)]="otp" [length]="4" [direction]="'rtl'" (completed)="onCompleted($event)"> </lib-ngx-otp-inputs> <p>Value: {{ otp }}</p>import { NgModule } from "@angular/core"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { BrowserModule } from "@angular/platform-browser"; import { AppComponent } from "./app.component"; import { NgxOtpInputsComponent } from "ngx-otp-inputs"; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, FormsModule, ReactiveFormsModule, NgxOtpInputsComponent], bootstrap: [AppComponent], }) export class AppModule {}| Input | Type | Default | Description |
|---|---|---|---|
length | number | 4 | Number of OTP digits |
direction | 'ltr' | 'rtl' | 'ltr' | Input direction |
disabled | boolean | false | Disables all OTP inputs |
readonly | boolean | false | Makes inputs read-only |
maskInput | boolean | false | Masks each character as a password |
autoSubmit | boolean | false | Emits completed automatically |
autoBlur | boolean | false | Blurs last input on completion |
autoFocus | boolean | true | Automatically focuses the first input |
inputType | 'number' | 'text' | 'alphanumeric' | 'number' | Input filtering pattern |
inputMode | 'numeric' | 'text' | 'decimal' | 'tel' | 'email' | 'numeric' | Helps mobile keyboards |
inputClass | string | 'otp-input' | Custom CSS class for each OTP input |
wrapperClass | string | 'otp-wrapper' | Custom CSS class for the wrapper |
ariaLabels | string[] | [] | Accessibility labels for each input |
status | 'success' | 'failed' | null | null | Visual state (adds success/error border colors) |
| Output | Type | Description |
|---|---|---|
completed | EventEmitter<string> | Emits OTP when all fields filled |
changed | EventEmitter<string> | Emits OTP on every input change |
@ViewChild(NgxOtpInputsComponent) otp!: NgxOtpInputsComponent; // Clear all inputs this.otp.reset();You can fully style the component using CSS variables:
| Variable | Default |
|---|---|
--ngx-otp-width | 48px |
--ngx-otp-height | 56px |
--ngx-otp-border-radius | 8px |
--ngx-otp-border-color | #ccc |
--ngx-otp-focus-border-color | #1976d2 |
--ngx-otp-font-size | 18px |
--ngx-otp-bg | #fff |
--ngx-otp-text-color | #000 |
--ngx-otp-gap | 8px |
- Use
[inputMode]="'numeric'"for numeric keyboard on mobile. - For SMS OTP auto-fill, the first input uses
autocomplete="one-time-code".
- For numeric OTPs, prefer
[inputType]="'number'". - Fix
lengthand rely on form validation. - Prefer
inputMode="numeric"overtype="number"on mobile.
# Clone repository git clone https://github.com/magdy-abas/ngx-otp-inputs cd ngx-otp-inputs # Run demo locally cd demo npm i npm start # Build the library cd ../projects/ngx-otp-inputs npm run buildMIT License β free for commercial and personal use.
PRs & issues are welcome!
Q: Why not use a single <input> with a mask?
A: Multi-input UX gives clearer visual progress and works better with paste + mobile keyboards.
Q: Does it work with SSR?
A: Yes. The component avoids direct DOM globals, so itβs SSR-friendly.
Q: How do I programmatically set the value?
A: If youβre using Reactive Forms, patchValue({ otp: '123456' }) will distribute characters automatically.