Angular One Time Password (OTP) Input Component

One Time Password

CoreUI PRO
This component is part of CoreUI PRO – a powerful UI library with over 250 components and 25+ templates, designed to help you build modern, responsive apps faster. Fully compatible with Angular, Bootstrap, React.js, and Vue.js.

Develop secure and user-friendly Angular one-time password input fields with automatic navigation, paste support, validation, and customizable options for modern authentication flows.

Added in v5.5.23

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-label attributes
  • 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:

KeyAction
ArrowLeftMoves focus to previous field
ArrowRightMoves focus to next field
BackspaceDelete current character or move to a previous field (if empty)
TabMove to next focusable element (respects linear mode)
Shift + TabMove to previous focusable element
Ctrl + V / Cmd + VPaste 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

scss
$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

ts
import { NgModule } from '@angular/core';
import { OneTimePasswordModule } from '@coreui/angular';

@NgModule({
  imports: [OneTimePasswordModule]
})
export class CustomAppModule {}

OTP Standalone

ts
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


jsx
import { OneTimePasswordComponent } from '@coreui/angular-pro'

Props

PropertyDefaultType
ariaLabel(index, total) => `Digit ${index + 1} of ${total}`object

Function to generate aria-label for each input field. Receives current index (0-based) and total number of inputs.

autoSubmitfalseboolean

Automatically submit the form when all one-time password fields are filled.

defaultValueundefinedstring, number

Initial value for Angular one-time password input.

disabledfalseboolean

Disable all one-time password (OTP) input fields.

lineartrueboolean

Enforce sequential input (users must fill fields in order).

maskedfalseboolean

Show input as a password (masked characters).

placeholder''string

Placeholder text for input fields. Single character applies to all fields, longer strings apply character-by-character.

readOnlyfalseboolean

Make the Angular OTP input component read-only.

sizingundefined'', 'sm', 'lg'

Sets the visual size of the Angular one-time password (OTP) input. Use 'sm' for small or 'lg' for large input fields.

type'number''number', 'text'

Input validation type: 'number' for digits only, or 'text' for free text.

validundefinedboolean

Set form input validation state to valid.

valueundefinedstring, number

Current value for OTP input.

Events

Event name
complete

Callback triggered when all Angular one-time password (OTP) fields are filled.

  • $event string
valueChange

Event triggered when the Angular one-time password (OTP) value changes.

  • $event string

input[cOtp]

directive


jsx
import { OtpDirective } from '@coreui/angular-pro'
PropertyDefaultType
disabledfalseboolean

Toggle the disabled state for the slot. Disabled slots are skipped when moving focus between them.

placeholder''string

Placeholder for the slot. A string longer than one character is spread across the slots, one character each; when unset the parent component's placeholder is used.

value''string, number

Value of the slot, kept in sync with the input element.