Develop secure and user-friendly Angular one-time password input fields with automatic navigation, paste support, validation, and customizable options for modern authentication flows.
Example
The c-one-time-password component and cOtp directive are perfect for creating secure Angular OTP input fields. They offer automatic character navigation and validation, making them ideal for two-factor authentication, SMS verification codes, and secure login flows.
import { Component, signal } from '@angular/core';
import { FormLabelDirective, OneTimePasswordModule } from '@coreui/angular';
@Component({
selector: 'docs-otp',
imports: [FormLabelDirective, OneTimePasswordModule],
templateUrl: './otp.component.html'
})
export class OtpComponent {
readonly value = signal('');
}<label cLabel for="otp01">Enter OTP Code</label>
<c-one-time-password (valueChange)="value.set($event)" id="otp01">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
<span class="text-body-secondary small"> Current value: {{ value() }}</span>One-time password types
The one-time password input supports various input types for different use cases.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-one-time-password-types',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-one-time-password-types.component.html'
})
export class OtpOneTimePasswordTypesComponent {}<div class="mb-3">
<label cLabel for="otp02a">Numeric OTP (default)</label>
<c-one-time-password id="otp02a" type="number">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp02b">Text OTP</label>
<c-one-time-password id="otp02b" type="text">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<form>
<label cLabel for="otp02c">Masked OTP (hidden characters)</label>
<c-one-time-password id="otp02c" masked>
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</form>
</div>Placeholders
Provide visual cues to users by displaying placeholder text in the input fields for OTP. You can use a single character for all fields or specify different placeholders for each field.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-placeholders',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-placeholders.component.html'
})
export class OtpPlaceholdersComponent {}<div class="mb-3">
<label cLabel for="otp03a">Single character placeholder</label>
<c-one-time-password id="otp03a" placeholder="_">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp03b">Different placeholders per field</label>
<c-one-time-password id="otp03b" placeholder="123456">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp03c">Letter placeholders</label>
<c-one-time-password id="otp03c" placeholder="ABCDEF" type="text">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>Pre-filled values
Set initial values using the value prop.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-pre-filled-values',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-pre-filled-values.component.html'
})
export class OtpPreFilledValuesComponent {}<label cLabel for="otp04">OTP with pre-filled value</label>
<c-one-time-password defaultValue="654321" id="otp04">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>Input modes
Control user input behavior with linear and non-linear modes.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-input-modes',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-input-modes.component.html'
})
export class OtpInputModesComponent {}<div class="mb-3">
<label cLabel for="otp05a">Linear mode (sequential input)</label>
<c-one-time-password id="otp05a" [linear]="true">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp05b">Non-linear mode (free navigation)</label>
<c-one-time-password id="otp05b" [linear]="false">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>Auto-submit
Enable automatic form submission when all one-time password fields are completed.
import { Component } from '@angular/core';
import { ButtonDirective, FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'docs-otp-auto-submit',
imports: [
FormLabelDirective,
OneTimePasswordComponent,
OtpDirective,
ButtonDirective,
FormsModule,
ReactiveFormsModule
],
templateUrl: './otp-auto-submit.component.html'
})
export class OtpAutoSubmitComponent {
readonly otpForm = new FormGroup({
otp: new FormControl<string | number | null>('', { validators: [Validators.required, Validators.minLength(6)] })
});
protected onSubmit($event: SubmitEvent) {
$event.preventDefault();
if (this.otpForm.valid) {
const otpValue = this.otpForm.get('otp')?.value;
console.log('Submitting:', otpValue);
alert(`Form submitted: ${otpValue}`);
this.otpForm.reset();
}
}
}<form #otpFormRef (ngSubmit)="onSubmit($event)" [formGroup]="otpForm">
<div class="mb-3">
<label cLabel for="otp06">Auto-submit OTP (fill all 6 digits)</label>
<c-one-time-password autoSubmit formControlName="otp" id="otp06">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<button cButton type="submit">Submit</button>
</form>Custom layouts
Create custom one-time password layouts with separators and different field counts.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-custom-layouts',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-custom-layouts.component.html'
})
export class OtpCustomLayoutsComponent {}<div class="mb-3">
<label cLabel for="otp07a">6-digit OTP with separators</label>
<c-one-time-password id="otp07a" type="number">
<input cOtp />
<input cOtp />
<input cOtp />
<div class="px-2 text-body-tertiary fw-bold">-</div>
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp07b">9-digit OTP with separators</label>
<c-one-time-password id="otp07b" type="text">
<input cOtp />
<input cOtp />
<input cOtp />
<div class="px-2 text-body-tertiary fw-bold">•</div>
<input cOtp />
<input cOtp />
<input cOtp />
<div class="px-2 text-body-tertiary fw-bold">•</div>
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<form>
<label cLabel for="otp07c">4-digit PIN</label>
<c-one-time-password id="otp07c" masked>
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</form>
</div>Sizing variants
One-time password input supports different sizes. You may choose from small, normal (default), and large inputs to match our similarly sized text inputs.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-sizing-variants',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-sizing-variants.component.html'
})
export class OtpSizingVariantsComponent {}<div class="mb-3">
<label cLabel for="otp08a">Large OTP input</label>
<c-one-time-password id="otp08a" sizing="lg">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp08b">Default OTP input</label>
<c-one-time-password id="otp08b">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp08c">Small OTP input</label>
<c-one-time-password id="otp08c" sizing="sm">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>Disabled state
Disable the entire one-time password input by adding the disabled prop.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-disabled-state',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-disabled-state.component.html'
})
export class OtpDisabledStateComponent {}<label cLabel for="otp09">Disabled OTP</label>
<c-one-time-password id="otp09" disabled defaultValue="0123456">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>Readonly state
Use the readOnly prop to make the one-time password input non-editable but still selectable.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-readonly-state',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-readonly-state.component.html'
})
export class OtpReadonlyStateComponent {}<label cLabel for="otp10">Readonly OTP input</label>
<c-one-time-password id="otp10" readOnly defaultValue="098765">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>Validation state
Use the valid prop to indicate input validity.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-validation-state',
imports: [OneTimePasswordComponent, FormLabelDirective, OtpDirective],
templateUrl: './otp-validation-state.component.html'
})
export class OtpValidationStateComponent {}<div class="mb-3">
<label cLabel for="otp13a">Valid OTP Input</label>
<c-one-time-password id="otp13a" [valid]="true">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp13b">Invalid OTP Input</label>
<c-one-time-password id="otp13b" [valid]="false">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
<div class="mb-3">
<label cLabel for="otp13c">OTP Input without validation state</label>
<c-one-time-password id="otp13c" [valid]="undefined">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>Forms
Angular handles user input through reactive and template-driven forms. CoreUI One Time Password OTP Input for Angular supports both approaches.
Reactive
The Angular OTP Input component can be used with reactive forms. You can bind the OTP Input value to a form control using the formControlName directive.
import { Component } from '@angular/core';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { JsonPipe } from '@angular/common';
@Component({
selector: 'docs-otp-reactive',
imports: [FormLabelDirective, OneTimePasswordComponent, OtpDirective, ReactiveFormsModule, JsonPipe],
templateUrl: './otp-reactive.component.html'
})
export class OtpReactiveComponent {
readonly otpForm = new FormGroup({
otp: new FormControl<string | number | null>('', { validators: [Validators.required, Validators.minLength(6)] })
});
}<form [formGroup]="otpForm">
<div class="mb-3">
<label cLabel for="otp11">OTP input with Angular reactive forms</label>
<c-one-time-password formControlName="otp" id="otp11">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
</form>
<span class="text-body-secondary small"> Current value: {{ otpForm.value | json }}</span>
Template-driven
The Angular OTP Input component can be used in template-driven forms. You can bind the value to a template variable using the ngModel directive.
import { JsonPipe } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormLabelDirective, OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
selector: 'docs-otp-template-driven',
imports: [FormsModule, FormLabelDirective, JsonPipe, OneTimePasswordComponent, OtpDirective],
templateUrl: './otp-template-driven.component.html'
})
export class OtpTemplateDrivenComponent {}<form #otpForm= "ngForm" >
<div class="mb-3">
<label cLabel for="otp12">OTP input with Angular template-driven forms</label>
<c-one-time-password ngModel name="otp" id="otp12">
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
</div>
</form>
<span class="text-body-secondary small"> Current value: {{ otpForm.value | json }}</span>
Accessibility (a11y)
The one-time password input component is designed with accessibility in mind and follows WCAG guidelines for form inputs.
- ARIA Labels: Each input field automatically receives descriptive
aria-labelattributes - Role Attribute: The container has
role="group"to indicate related form controls - Keyboard Navigation: Full keyboard support with arrow keys, tab, and backspace
- Screen Reader Support: Clear announcements when values change or validation occurs
- Focus Management: Automatic focus handling for seamless navigation
Customizing accessibility
You can customize the aria-label generation function to provide more specific descriptions:
const ariaLabel = (index, total) => Enter digit ${index + 1} of your ${total}-digit verification code
Keyboard support
The one-time password input component provides comprehensive keyboard navigation support:
| Key | Action |
|---|---|
| ArrowLeft | Moves focus to previous field |
| ArrowRight | Moves focus to next field |
| Backspace | Delete current character or move to a previous field (if empty) |
| Tab | Move to next focusable element (respects linear mode) |
| Shift + Tab | Move to previous focusable element |
| Ctrl + V / Cmd + V | Paste clipboard content and auto-fill fields |
Customizing
CSS Classes
The One Time Password Input components use the following CSS classes:
.form-otp- Applied to the wrapper container.form-otp-sm- Small size variant.form-otp-lg- Large size variant.form-otp-control- Applied to individual input elements
SASS variables
$form-otp-gap: .125rem !default;
$form-otp-control-width: 2rem !default;
$form-otp-control-padding-y: $input-padding-y !default;
$form-otp-control-padding-x: 0 !default;
$form-otp-control-font-family: $input-font-family !default;
$form-otp-control-font-size: $input-font-size !default;
$form-otp-control-font-weight: $input-font-weight !default;
$form-otp-control-line-height: $input-line-height !default;
$form-otp-control-color: $input-color !default;
$form-otp-control-bg: $input-bg !default;
$form-otp-control-border-width: $input-border-width !default;
$form-otp-control-border-color: $input-border-color !default;
$form-otp-control-border-radius: $input-border-radius !default;
$form-otp-control-box-shadow: $input-box-shadow !default;
$form-otp-control-transition: $input-transition !default;
$form-otp-control-focus-color: $input-focus-color !default;
$form-otp-control-focus-bg: $input-focus-bg !default;
$form-otp-control-focus-border-color: $input-focus-border-color !default;
$form-otp-control-focus-box-shadow: $input-focus-box-shadow !default;
$form-otp-control-width-sm: 1.5rem !default;
$form-otp-control-padding-y-sm: $input-padding-y-sm !default;
$form-otp-control-padding-x-sm: 0 !default;
$form-otp-control-font-size-sm: $input-font-size-sm !default;
$form-otp-control-border-radius-sm: $input-border-radius-sm !default;
$form-otp-control-width-lg: 2.5rem !default;
$form-otp-control-padding-y-lg: $input-padding-y-lg !default;
$form-otp-control-padding-x-lg: 0 !default;
$form-otp-control-font-size-lg: $input-font-size-lg !default;
$form-otp-control-border-radius-lg: $input-border-radius-lg !default;API reference
OTP Module
import { NgModule } from '@angular/core';
import { OneTimePasswordModule } from '@coreui/angular';
@NgModule({
imports: [OneTimePasswordModule]
})
export class CustomAppModule {}OTP Standalone
import { Component } from '@angular/core';
import { OneTimePasswordComponent, OtpDirective } from '@coreui/angular';
@Component({
template: `
<c-one-time-password>
<input cOtp />
<input cOtp />
<input cOtp />
<input cOtp />
</c-one-time-password>
`,
imports: [OneTimePasswordComponent, OtpDirective]
})
export class CustomAppComponent {}c-one-time-password
component
import { OneTimePasswordComponent } from '@coreui/angular-pro'Props
Events
input[cOtp]
directive
import { OtpDirective } from '@coreui/angular-pro'