OTP / PIN Input

Free

4, 5 and 6-digit OTP/PIN inputs with auto-advance, paste support, arrow-key navigation, mask mode and status variants.

</>sv-otp-input otppinsecurity
import { SvOtpInputComponent } from '@styloviz/forms';

Demo & usage

Basic usage

Usage example — open the gallery for the full interactive demo.

An interactive demo for this component lives in the gallery.

Open live demo

More examples

Masked PIN with error state

example.htmlAngular
<sv-otp-input [length]="4" [mask]="true" status="error" hint="Incorrect code" (complete)="check($event)" />

Reactive forms

Implements ControlValueAccessor; the bound value hydrates the cells, so initial values and resets render.

example.htmlAngular
<sv-otp-input formControlName="code" label="Verification code" [length]="6" (complete)="verify($event)" />

When to use

  • Entering a short verification code or PIN (2FA, email confirmation, transactions).
  • You want auto-advance, paste-the-whole-code and per-cell navigation.
  • For longer secrets use a password Text Field instead.

Async states

None — this component has nothing to load, so it has no loading, empty or error state to drive.

Inputs

NameTypeDefaultDescription
length4 | 5 | 66Number of code cells.
valuestring (two-way)''Current code value (two-way).
variantOtpInputVariant'default'Visual variant of the cells.
sizeOtpInputSize'md'Cell size: sm, md or lg.
statusOtpInputStatus'idle'Validation status: idle, error, success or loading.
maskbooleanfalseMask entered digits (password-style).
disabledbooleanfalseDisable all cells.
labelstring''Accessible label for the code group.
hintstring''Helper/error text shown below the cells.
autoFocusbooleanfalseFocus the first cell on render.
modeOtpInputMode'cells'How the code is actually taken from the user.
webOtpbooleanfalseAsk the browser to read the code out of an incoming SMS.
clearOnErrorbooleantrueWipe the code when it is rejected, so the user is not editing a code that has already been refused.
errorMessagestring''Client-side validation message.
serverErrorSvServerErrornullA rejection reported by the server about this value. Self-expiring.

Outputs

EventPayload typeDescription
completestringEmitted with the full code once every cell is filled.
changedstringEmitted on every change to the partial code.
codeClearedvoidEmitted when a rejected code was wiped — see `clearOnError`.
valueChangestringEmits the new `value` whenever it changes — the output half of `[(value)]`.

Customization & theming

  • Configure `length` (4–6), `variant` (default · filled · underline · ghost), `size` and optional `mask` mode.
  • Error / success / loading `status` with a `hint` message; `label`, `disabled`, `autoFocus`.
  • Two-way bind `[(value)]` or a reactive control (CVA); listen to `(complete)` and `(changed)`.

Best practices

  • Do: Handle the `complete` event to verify as soon as the final digit lands.
  • Do: Support pasting the full code — users copy it from email/SMS.
  • Tip: Use `mask` only for sensitive PINs; verification codes are usually fine visible.

Accessibility

  • Each cell is a labelled numeric input (`inputmode="numeric"`, `autocomplete="one-time-code"`) with arrow-key, Backspace/Delete navigation and full-code paste.
  • The group is labelled and `aria-describedby`-linked to its hint; status/error/success messages are announced through an `aria-live` region (assertive on error).
  • Cells set `aria-invalid` when `status="error"`; the bound value hydrates the cells so controlled/reset states render correctly.

Feedback on OTP / PIN Input

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.