AppOtpInput
Code entry for MFA, email and phone verification.
<app-otp-input label="Verification code" name="Code" /><app-otp-input> is the field a user types a one-time code into — signing in with MFA, confirming an email or phone, redeeming a backup code.
It is one input with the segment boxes drawn underneath, not one input per character. That is the whole design and it is not cosmetic: it is what makes phone autofill and screen readers work.
Playground
Every attribute, live. Each change re-renders the real tag helper on the server, so the markup underneath is the exact call that produced the field above it. Type or paste into the field to see the segments fill.
<input> imperatively (document.querySelector('wui-otp input').focus()), the same way any custom element is focused.Basic
Six digits, the default.
Show code
<app-otp-input label="Verification code" name="Code" />
123456 and paste it anywhere in the field above — even with stray spaces or dashes (12 34-56). There is no special paste handler to configure: it is one <input>, so the browser hands it the whole clipboard string and the element strips what the mode does not allow before filling the segments.States
Length, mode, and the states a verify round-trip goes through. The same label / required / hint / error contract as every other field.
Show code
<app-otp-input label="Backup code" name="B1" length="8" mode="alphanumeric" /> <app-otp-input label="Verification code" name="B2" error="That code has expired." /> <app-otp-input label="Verification code" name="B3" disabled="true" />
When to use
Use it when
- A one-time code: MFA, email or phone verification, backup codes.
- Anywhere the code has a known, fixed length.
Reach for something else when
- A password or passphrase. → app-input with type="password"
- A code of unknown or variable length. → app-input
- A licence key with separators — the segments here are characters, not groups. → app-input
Best practices
Every option
The whole surface of the component, one cell per value.
length — every length
length="4"
length="6"
length="8"
mode — numeric vs alphanumeric
mode="numeric"
mode="alphanumeric"
state
(default)required="true"disabled="true"disabled="true" value="482913"error="…"hint="…"error + hint (error wins)shell — label / hint / error vs bare
(no label, no hint, no error) — bare element, no <div class="field"> wrapperlabel onlylabel + requiredAttributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| asp-for | ModelExpression | — | Model property to bind to — supplies name, id, value, label and required. |
| label | string | — | Field label, and the control's accessible name. |
| name | string | — | POST binding name. |
| id | string | — | Explicit control id — routed to the single visible input; defaults from name. |
| value | string | — | The code entered so far. |
| length | int | 6 | Characters in the code. TOTP is 6; backup codes are often 8. |
| mode | numeric | alphanumeric | numeric | Drives the on-screen keyboard and what the field will accept. |
| required | bool | — | Marks the field required, in the markup and to assistive tech. |
| hint | string | — | Helper text under the field. |
| error | string | — | Replaces the hint and marks the field invalid. |
| disabled | bool | — | Disable the control. |
| class | string | — | Extra classes on the control (React className). |
| input-class | string | — | Extra classes on the control itself (the wui-otp) — the React className counterpart. The plain class attribute merges onto the .field wrapper instead. |