otp-angular
v1.4.0
Published
Angular OTP input component for verification codes, 2FA, Web OTP, reactive forms, resend timer, accessibility, and custom OTP UI.
Maintainers
Keywords
Readme
OTP Angular
otp-angular is an Angular OTP input component for verification codes, 2FA/MFA login flows, Web OTP SMS auto-fill, resend countdowns, accessibility, and custom OTP UI. It works with Angular reactive forms, template-driven forms, and direct event binding.
Current version: 1.4.0
Angular compatibility: works with supported Angular versions listed in peer dependencies

Features
- Standalone Angular component: import
OtpAngulardirectly in a standalone component. - Reactive forms support: works with
formControl,formControlName, andFormGroup. - Template-driven forms support: works with
[(ngModel)]. - Manual event support: use
onInputChange,onAutoSubmit,onResendTimerChange, andonResendAvailable. - Configurable OTP length: choose 4 digits, 6 digits, or any custom length.
- Numeric-only mode: block non-numeric characters when
numbersOnlyis enabled. - Alphanumeric mode: allow letters and numbers by default.
- Uppercase mode: convert letters to uppercase with
showCaps. - Password mode: hide OTP characters with password-style inputs, with optional banking-style mask delay.
- Auto focus: automatically focus the first input when the component renders in the browser.
- Auto submit: emit the full OTP value when all boxes are filled, with optional valid-only submit control.
- Loading/verifying state: disable OTP boxes and show a compact spinner while verification is running.
- Web OTP SMS auto-fill: optionally fill OTP from supported Android Chrome SMS prompts with
webOtp. - Accessibility support: screen-reader labels, live error/resend announcements, and keyboard-only controls.
- Paste support: paste a full OTP and fill the boxes automatically.
- Keyboard navigation: supports Backspace, Delete, Home, End, ArrowLeft, and ArrowRight.
- Disabled state: works through Angular forms disable state and the public
disabledsignal. - Error state: show Angular form validation messages after touch, with custom messages for cases like invalid or expired OTP.
- Resend countdown: show a resend timer, emit every timer change, and emit when the resend action is clicked.
- Public methods: call
setValue(),clear(),focus(),focusIndex(index), andreset()from a parent component. - Custom input template: use
otpAngularInputTemplateandotpAngularInputto fully control each OTP box UI. - Custom styling: pass custom classes and inline styles for the container and input boxes.
- Per-input styling: pass arrays for input classes or styles.
- Separator support: show a separator between OTP inputs.
- Theme presets: built-in
default,banking,minimal,rounded,underline,glass,light, anddarkstyles. - Size presets: choose
sm,md, orlgOTP boxes without manually styling width, height, or font size. - Variant presets: choose
box,underline,filled, oroutlineinput structure. - Validation helpers: use
OtpValidators.complete(),OtpValidators.numeric(), andOtpValidators.length(size). - Manual error API: call
setError(message)andclearError()from a parent component. - Success state: show a verified state with
success.set(true)orsuccess: true. - Auto clear/focus helpers: use
clearOnErrorandfocusOnClearfor failed verification flows. - Multiple-instance safe: multiple OTP components can be used on the same page.
- SSR-safe: avoids browser-only focus, timer, and clipboard behavior on the server.
- No runtime dependency other than Angular and
tslib.
Live Demo And Docs
Open the full demo page here:
https://subha-patra.github.io/otp-angular/The page includes a feature gallery with live examples for reactive forms, template-driven forms, alphanumeric input, uppercase mode, theme presets, size presets, variant presets, validation helpers, manual errors, success state, auto clear/focus, disabled form controls, custom styling, loading/verifying state, form errors, password mask delay, resend timer events, public methods, and custom input templates. It also documents Web OTP, paste/keyboard behavior, accessibility labels, SSR safety, and tested Angular compatibility.
Installation
npm install otp-angularQuick Start
Standalone Component
import { Component, signal } from '@angular/core';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
selector: 'app-login',
imports: [OtpAngular],
template: `
<otp-angular
[config]="config()"
(onInputChange)="onInputChange($event)"
(onAutoSubmit)="verifyOtp($event)"
/>
`
})
export class LoginComponent {
config = signal<OtpAngularType>({
length: 6,
numbersOnly: true,
autoFocus: true,
autoSubmit: true,
autoSubmitValidOnly: true,
webOtp: true
});
onInputChange(value: string | number | null): void {
console.log(value);
}
verifyOtp(value: string | number | null): void {
console.log(value);
}
}Reactive Forms
import { Component, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
selector: 'app-otp-form',
imports: [OtpAngular, ReactiveFormsModule],
template: `
<otp-angular [config]="config()" [formControl]="otp" />
`
})
export class OtpFormComponent {
config = signal<OtpAngularType>({ length: 4, numbersOnly: true });
otp = new FormControl<string | number | null>('');
fillDemoValue(): void {
this.otp.setValue('1234');
}
}Template-Driven Forms
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
selector: 'app-ng-model-otp',
imports: [OtpAngular, FormsModule],
template: `
<otp-angular [config]="config" [(ngModel)]="otp" />
`
})
export class NgModelOtpComponent {
config: OtpAngularType = { length: 4 };
otp: string | number | null = '';
}Inputs, Outputs, And Methods
| API | Type | Required | Description |
| --- | --- | --- | --- |
| config | OtpAngularType | Yes | Main configuration object. |
| disabled | WritableSignal<boolean> | No | Programmatically disables or enables all boxes. |
| loading | WritableSignal<boolean> | No | Shows verifying state and blocks user input without disabling the Angular form control. |
| success | WritableSignal<boolean> | No | Shows or hides the success visual state from a parent component. |
| onInputChange | Output<string \| number \| null> | No | Emits whenever the OTP value changes. |
| onAutoSubmit | Output<string \| number \| null> | No | Emits when all boxes are filled and autoSubmit is true. |
| onResendTimerChange | Output<number> | No | Emits the remaining resend countdown seconds from the initial value through 0. |
| onResendAvailable | Output<boolean> | No | Emits true when the resend action is clicked. |
| setValue(value) | Method | No | Sets the visible OTP value from the parent component. |
| clear() | Method | No | Clears all boxes and updates the Angular form value to null. |
| focus() | Method | No | Focuses the first OTP box when enabled in the browser; no-op while disabled, loading, or SSR. |
| focusIndex(index) | Method | No | Focuses a zero-based OTP box index when enabled in the browser; invalid indexes, disabled/loading state, and SSR are ignored. |
| reset() | Method | No | Restarts the resend countdown. |
| setError(message) | Method | No | Shows a manual parent-controlled error message. |
| clearError() | Method | No | Clears only the manual parent-controlled error message. |
| OtpAngularInputTemplate | Directive | No | Place on an ng-template inside <otp-angular> to customize each rendered OTP box. |
| OtpAngularInput | Directive | No | Place on the real focusable input inside a custom template so focus, paste, arrow keys, and public focus methods keep working. |
| OtpAngularInputContext | Type | No | Type for the custom template context passed as let-otp. |
Config Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| length | number | 4 | Number of OTP boxes. |
| numbersOnly | boolean | false | Allows only numeric input. |
| autoSubmit | boolean | false | Emits onAutoSubmit when the OTP is complete. |
| autoSubmitValidOnly | boolean | false | When autoSubmit is true, only emits after accepted input with no rejected or truncated characters and no invalid Angular form state. |
| autoFocus | boolean | false | Focuses the first input in the browser after render. |
| webOtp | boolean | false | Enables Web OTP API SMS auto-fill on supported secure browsers. |
| isPassword | boolean | false | Uses password input type to hide characters. |
| maskDelay | number | 0 | When isPassword is true, briefly shows newly typed or pasted characters for this many milliseconds before masking them again. |
| showError | boolean | false | Adds error styling to empty boxes on blur. |
| errorMessages | Record<string, string> | {} | Custom messages for Angular form errors such as invalidOtp or otpExpired. |
| errorMessageClass | string | '' | CSS class added to the rendered validation message. |
| loadingLabel | string | Verifying... | Text shown beside the loading spinner. |
| loadingContainerClass | string | '' | CSS class added to the loading status row. |
| loadingSpinnerClass | string | '' | CSS class added to the loading spinner. |
| ariaLabel | string | One-time password | Accessible label for the full OTP input group. |
| inputAriaLabel | string | One-time password digit {index} of {length} | Accessible label template for each OTP input. Supports {index} and {length}. |
| resendAriaLabel | string | Resend verification code | Accessible label for the resend action button. |
| resendCountdownAriaLabel | string | Resend verification code available in {seconds} seconds | Live countdown announcement template. Supports {seconds} and {label}. |
| showCaps | boolean | false | Converts letters to uppercase. |
| containerClass | string \| string[] | '' | CSS class or classes for the OTP container. |
| containerStyles | object | {} | Inline styles for the OTP container. |
| inputClass | string \| string[] | '' | CSS class or classes for input boxes. |
| inputStyles | object \| object[] | {} | Inline styles for input boxes. |
| placeholder | string | '' | Placeholder shown inside each input. |
| separator | string | '' | Character shown between input boxes. |
| resend | number | 0 | Enables resend countdown when greater than 0. |
| resendLabel | string | RESEND VERIFICATION CODE | Text shown for resend action/timer. |
| resendContainerClass | string | '' | CSS class for the resend wrapper. |
| resendLabelClass | string | '' | CSS class for the resend action label. |
| resendTimerClass | string | '' | CSS class for the countdown text. |
| theme | 'default' \| 'banking' \| 'minimal' \| 'rounded' \| 'underline' \| 'glass' \| 'light' \| 'dark' | 'default' | Built-in theme preset. light is kept as a backward-compatible alias of default. |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Built-in input size preset for OTP box width, height, font size, placeholder size, and separator size. |
| variant | 'box' \| 'underline' \| 'filled' \| 'outline' | 'box' | Built-in input structure preset. |
| success | boolean | false | Shows success styling and success status text. |
| successLabel | string | Verified | Text shown in the success status row. |
| clearOnError | boolean | false | Clears the OTP value when setError(message) is called. |
| focusOnClear | boolean | false | Focuses the first OTP box after clear() or clearOnError. |
Theme Presets
Use theme when you want a polished OTP UI without writing custom CSS. Existing containerClass, containerStyles, inputClass, and inputStyles still work on top of a preset.
config: OtpAngularType = {
length: 6,
numbersOnly: true,
theme: 'banking'
};Available presets:
| Theme | Best for |
| --- | --- |
| default | Clean neutral OTP boxes and the documented baseline style. |
| banking | Stronger borders and focus emphasis for fintech, payment, and secure login flows. |
| minimal | Quiet forms where surrounding UI should stay visually dominant. |
| rounded | Softer mobile-first verification screens. |
| underline | Borderless inputs with a bottom rule only. |
| glass | Modern translucent app surfaces. |
| light | Backward-compatible alias of default. |
| dark | Dark panels and app shells. |
Size Presets
Use size when the UI only needs smaller or larger OTP boxes, not a full custom style override.
config: OtpAngularType = {
length: 6,
numbersOnly: true,
size: 'lg'
};Available presets:
| Size | Best for |
| --- | --- |
| sm | Compact forms, drawers, side panels, and dense layouts. |
| md | Default OTP size and the familiar baseline layout. |
| lg | Mobile-first login screens and high-emphasis verification flows. |
Custom inputClass, inputStyles, containerClass, and containerStyles still work on top of a size preset.
Variant Presets
Use variant when the OTP boxes need a different structure but the same theme and size.
config: OtpAngularType = {
length: 6,
numbersOnly: true,
variant: 'filled'
};Available variants:
| Variant | Best for |
| --- | --- |
| box | Default boxed OTP inputs for most forms. |
| underline | Quiet bottom-border-only OTP inputs. |
| filled | Soft filled OTP boxes for dense app forms. |
| outline | Strong outline OTP boxes for high-emphasis flows. |
theme, size, variant, custom classes, and inline styles can be combined safely.
Validation Helpers
Use OtpValidators with Angular reactive forms when you want reusable OTP validation rules.
import { FormControl } from '@angular/forms';
import { OtpValidators } from 'otp-angular';
otp = new FormControl('', [
OtpValidators.complete(6),
OtpValidators.numeric(),
OtpValidators.length(6)
]);Validator error keys are otpIncomplete, otpNumeric, and otpLength.
Manual Error And Success State
Use the manual error API when server verification fails but you do not want to manually manage Angular form errors.
otpRef.setError('Invalid code');
otpRef.clearError();Use the success signal after your backend accepts the code.
otpRef.success.set(true);You can also configure success text:
config: OtpAngularType = {
length: 6,
successLabel: 'Verified'
};Auto Clear And Focus
Use clearOnError with focusOnClear when failed verification should clear the code and put the user back in the first box.
config: OtpAngularType = {
length: 6,
numbersOnly: true,
clearOnError: true,
focusOnClear: true
};
otpRef.setError('Invalid code');Public Method Example
import { Component, ViewChild, signal } from '@angular/core';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
imports: [OtpAngular],
template: `
<otp-angular [config]="config()" />
<button type="button" (click)="fillOtp()">Fill</button>
<button type="button" (click)="clearOtp()">Clear</button>
<button type="button" (click)="focusOtp()">Focus</button>
<button type="button" (click)="focusThirdBox()">Focus third box</button>
<button type="button" (click)="showError()">Show error</button>
<button type="button" (click)="showSuccess()">Show success</button>
<button type="button" (click)="disableOtp()">Disable</button>
`
})
export class DemoComponent {
@ViewChild(OtpAngular) otpRef!: OtpAngular;
config = signal<OtpAngularType>({ length: 4, resend: 30 });
fillOtp(): void {
this.otpRef.setValue('1234');
}
clearOtp(): void {
this.otpRef.clear();
}
focusOtp(): void {
this.otpRef.focus();
}
focusThirdBox(): void {
this.otpRef.focusIndex(2);
}
disableOtp(): void {
this.otpRef.disabled.set(true);
}
resetResendTimer(): void {
this.otpRef.reset();
}
showError(): void {
this.otpRef.setError('Invalid code');
}
showSuccess(): void {
this.otpRef.clearError();
this.otpRef.success.set(true);
}
}Password Mode With Mask Delay
Use isPassword to mask OTP boxes. Add maskDelay when you want newly typed or pasted characters to show briefly before they are hidden again.
import { Component, signal } from '@angular/core';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
imports: [OtpAngular],
template: `<otp-angular [config]="config()" />`
})
export class PasswordOtpComponent {
config = signal<OtpAngularType>({
length: 6,
numbersOnly: true,
isPassword: true,
maskDelay: 500
});
}maskDelay works only when isPassword is true. Programmatic values from forms, setValue(), and Web OTP stay masked immediately.
Custom Input Template
Use otpAngularInputTemplate when the default input markup is not enough. The template receives one context object per box as let-otp. Put otpAngularInput on the real input element so the component can still manage focus, paste, Backspace, arrow keys, focus(), and focusIndex(index).
import { Component, signal } from '@angular/core';
import { OtpAngular, OtpAngularInput, OtpAngularInputTemplate, OtpAngularType } from 'otp-angular';
@Component({
imports: [OtpAngular, OtpAngularInput, OtpAngularInputTemplate],
template: `
<otp-angular [config]="config()">
<ng-template otpAngularInputTemplate let-otp>
<input
otpAngularInput
maxlength="1"
autocomplete="one-time-code"
[value]="otp.value"
[disabled]="otp.disabled"
[readOnly]="otp.readonly"
[type]="otp.type"
[placeholder]="otp.placeholder"
[class]="otp.inputClass"
[style]="otp.inputStyle"
[class.pin-box]="true"
[attr.aria-label]="otp.ariaLabel"
[attr.aria-invalid]="otp.ariaInvalid"
[attr.aria-describedby]="otp.ariaDescribedBy"
[attr.inputmode]="otp.inputMode"
[attr.pattern]="otp.pattern"
[class.error]="otp.hasError"
(keydown)="otp.handlers.keydown($event)"
(blur)="otp.handlers.blur($event)"
(input)="otp.handlers.input($event)"
(paste)="otp.handlers.paste($event)"
/>
</ng-template>
</otp-angular>
`
})
export class CustomOtpComponent {
config = signal<OtpAngularType>({
length: 6,
numbersOnly: true,
autoSubmit: true
});
}The context also includes index, loading, componentDisabled, blocked, inputClass, inputStyle, separator, isLast, maxlength, autocomplete, and name for richer custom UIs.
Custom Resend Timer UI
Use onResendTimerChange when your app wants to show the countdown outside the built-in resend row. The built-in resend UI still works as before.
import { Component, signal } from '@angular/core';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
imports: [OtpAngular],
template: `
<otp-angular
[config]="config()"
(onResendTimerChange)="remainingSeconds.set($event)"
(onResendAvailable)="resendCode()"
/>
<p>Try again in {{ remainingSeconds() }} seconds</p>
`
})
export class CustomResendComponent {
config = signal<OtpAngularType>({ length: 6, resend: 30 });
remainingSeconds = signal(30);
resendCode(): void {
console.log('Resend clicked');
}
}Web OTP SMS Auto-Fill
Enable browser-assisted SMS OTP filling with webOtp: true:
config: OtpAngularType = {
length: 6,
numbersOnly: true,
autoSubmit: true,
webOtp: true
};Web OTP works mainly on supported Android Chrome browsers and requires a secure context such as HTTPS. Your backend must send a Web OTP formatted SMS that includes the site domain and OTP code. Unsupported browsers, denied permission, SSR, insecure pages, and iframes without Permissions-Policy: otp-credentials support fall back to normal manual typing and paste behavior.
Strict Auto Submit
Use autoSubmitValidOnly when your app should auto-submit only after clean, accepted input. Rejected paste characters still update the visible OTP from the accepted part, but onAutoSubmit will not fire for that paste.
config: OtpAngularType = {
length: 6,
numbersOnly: true,
autoSubmit: true,
autoSubmitValidOnly: true
};For example, pasting 12a345 into a numeric OTP can fill the accepted digits, but the component will wait for a clean completion before emitting onAutoSubmit.
Accessibility Labels
The component includes a labelled OTP group, per-box labels, assertive form-error announcements, and polite resend countdown announcements. You can customize the screen-reader text without changing the visible UI.
config: OtpAngularType = {
length: 6,
numbersOnly: true,
ariaLabel: 'Login verification code',
inputAriaLabel: 'Verification code digit {index} of {length}',
resendAriaLabel: 'Send a new verification code',
resendCountdownAriaLabel: 'New code available in {seconds} seconds'
};Keyboard users can move with ArrowLeft and ArrowRight, jump with Home and End, remove the current box with Delete, and use Backspace as usual.
Loading / Verifying State
Use the public loading signal when your app starts server-side verification. This keeps the visible OTP value in place, blocks user typing and paste, and shows a small status row until you turn loading off.
import { Component, ViewChild, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
imports: [OtpAngular, ReactiveFormsModule],
template: `
<otp-angular
[config]="config()"
[formControl]="otp"
(onAutoSubmit)="verifyOtp($event)"
/>
`
})
export class VerifyOtpComponent {
@ViewChild(OtpAngular) otpRef!: OtpAngular;
config = signal<OtpAngularType>({
length: 6,
numbersOnly: true,
autoSubmit: true,
loadingLabel: 'Checking code...'
});
otp = new FormControl<string | number | null>('');
async verifyOtp(value: string | number | null): Promise<void> {
this.otpRef.loading.set(true);
try {
await this.verifyCodeOnServer(value);
} finally {
this.otpRef.loading.set(false);
}
}
private verifyCodeOnServer(value: string | number | null): Promise<void> {
return Promise.resolve();
}
}Form Error Messages
otp-angular can read Angular form errors from reactive forms and ngModel. It shows one message after the control is touched and invalid, and adds error styling plus aria-invalid to the OTP boxes.
import { Component, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { OtpAngular, OtpAngularType } from 'otp-angular';
@Component({
imports: [OtpAngular, ReactiveFormsModule],
template: `<otp-angular [config]="config()" [formControl]="otp" />`
})
export class VerifyOtpComponent {
config = signal<OtpAngularType>({
length: 6,
numbersOnly: true,
errorMessages: {
invalidOtp: 'Invalid OTP',
otpExpired: 'OTP expired'
}
});
otp = new FormControl<string | number | null>('', {
validators: [Validators.required, Validators.minLength(6)]
});
rejectCode(): void {
this.otp.markAsTouched();
this.otp.setErrors({ invalidOtp: true });
}
expireCode(): void {
this.otp.markAsTouched();
this.otp.setErrors({ otpExpired: true });
}
}Built-in messages are available for required, minlength, maxlength, pattern, invalidOtp, otpExpired, otpIncomplete, otpNumeric, and otpLength. If a key is not known and no custom message is configured, the component falls back to Invalid OTP.
Version 1.4.0 Notes
- Adds built-in theme presets for
default,banking,minimal,rounded,underline,glass,light, anddark. - Adds built-in size presets with
size: 'sm' | 'md' | 'lg'. - Adds variant presets with
variant: 'box' | 'underline' | 'filled' | 'outline'. - Adds
OtpValidators.complete(),OtpValidators.numeric(), andOtpValidators.length(size). - Adds parent-controlled
setError(message),clearError(), success state,clearOnError, andfocusOnClear. - Keeps
lightanddarkworking for backward compatibility while documentingdefaultas the baseline theme. - Keeps existing styling APIs working on top of theme, size, and variant presets.
- Updates the live demo/docs with copyable examples for every preset.
Version 1.3.1 Notes
- Adds the public GitHub Pages demo/docs site at
https://subha-patra.github.io/otp-angular/. - Updates package metadata and README links so npm users can find the live examples faster.
- Keeps the
1.3.xpublic API unchanged.
Version 1.3.0 Notes
- Adds optional Web OTP SMS auto-fill with
webOtp: true. - Uses the existing form/value pipeline, so Web OTP updates visible boxes, reactive forms,
ngModel,onInputChange, andonAutoSubmit. - Falls back silently when Web OTP is unsupported, unavailable, denied, aborted, or rendered on the server.
- Adds Angular-form-aware error messages for touched invalid controls.
- Supports custom
errorMessagesanderrorMessageClassconfig while keeping the existingshowErrorblur styling. - Adds a parent-controlled loading/verifying state with
otpRef.loading.set(true). - Adds public
clear(),focus(), andfocusIndex(index)methods for parent-controlled OTP flows. - Adds
onResendTimerChangeso parent apps can render custom resend timer UI from the same countdown. - Adds
otpAngularInputTemplateandotpAngularInputfor advanced custom OTP box markup without forking the library. - Adds
maskDelayfor password-mode OTP boxes, so newly typed or pasted characters can briefly show before masking. - Adds
autoSubmitValidOnlyso apps can block automatic submit after rejected or truncated paste input. - Improves accessibility with configurable screen-reader labels, assertive error announcements, polite resend countdown announcements, native resend button markup, and Home/End/Delete keyboard support.
Version 1.2.1 Notes
- Refreshes the npm README so users can see the full feature list, examples, config options, and release workflow directly on the package page.
- Keeps the same public API and tested Angular compatibility from
1.2.0.
Version 1.2.0 Notes
- Builds and tests with Angular 22.
- Keeps the tested Angular peer dependency range.
- Uses signal state for the OTP value.
- Uses Angular template refs instead of global DOM IDs.
- Removes internal helper directives for input filtering and disabled state.
- Fixes external form writes so
formControl.setValue(),[(ngModel)], andsetValue()update the visible boxes. - Emits
nullfor an empty numeric OTP instead of0. - Supports multiple OTP components on the same page without focus/value conflicts.
- Adds explicit SSR/browser guards for focus, timers, and legacy clipboard fallback.
- Expands tests for forms, paste, auto-submit, disabled state, multiple instances, keyboard behavior, and server-platform creation.
Updating The Version
Update the version in:
projects/otp-angular/package.jsonExample:
{
"name": "otp-angular",
"version": "1.4.0"
}Keep peer dependencies aligned with the currently tested Angular versions unless support changes:
{
"@angular/common": "^20.0.0 || ^21.0.0 || ^22.0.0",
"@angular/core": "^20.0.0 || ^21.0.0 || ^22.0.0",
"@angular/forms": "^20.0.0 || ^21.0.0 || ^22.0.0"
}Before publishing:
npm run ng -- build otp-angular
npm run ng -- test otp-angular --watch=false --browsers=ChromeHeadless --progress=false
npm run build
git diff --checkCheck the built package version:
node -p "require('./dist/otp-angular/package.json').version"Publish from the built package:
cd dist/otp-angular
npm publish --access public