Input OTP

An input for one-time passwords and verification codes, rendered as individual character slots

<dui-input-otp max-length="6"/>

Usage

The dui-input-otp Tag Helper renders a segmented input for one-time passwords and verification codes. It is backed by a single hidden <input> that holds the whole code — so it model-binds and posts back like any other text field — overlaid on the individual character slots. A small del-input-otp web component distributes what you type into the slots, moves the active slot and caret, and enforces the allowed character pattern.

Use the max-length attribute to control how many characters the code has.

<dui-input-otp max-length="6"/>

Input OTP only submits a value when you set asp-for or name. Just like a native input without a name, an Input OTP without one (such as most of the examples on this page) renders and is fully interactive but posts nothing. Use asp-for to model bind, or set name to post a value without binding.

Examples

Number of digits

Use the max-length attribute to set the number of slots. It defaults to 6.

<dui-input-otp max-length="4"/>

Groups

Use the groups attribute to split the slots into groups, separated by a separator. Provide a comma-separated list of group sizes — the sizes must add up to max-length (when both are set), otherwise the total is taken from the groups.

<dui-input-otp groups="3,3"/>

Separator

A separator is rendered automatically between groups. Supply more than two groups to add multiple separators.

<dui-input-otp groups="2,2,2"/>

Custom separator

Compose the slots by hand and supply child content to the dui-input-otp-separator Tag Helper to replace the default separator glyph. Any content works — here a dui-icon renders a different icon between the groups.

<dui-input-otp max-length="6">
    <dui-input-otp-group>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
    </dui-input-otp-group>
    <dui-input-otp-separator>
        <dui-icon name="dot" class="size-4"/>
    </dui-input-otp-separator>
    <dui-input-otp-group>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
    </dui-input-otp-group>
</dui-input-otp>

Size

Increase the size of the slots by applying utility classes to the groups. Target the slots from the group with the *:data-[slot=input-otp-slot]: variant so the sizing applies to every slot inside it.

<dui-input-otp max-length="6">
    <dui-input-otp-group class="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
    </dui-input-otp-group>
    <dui-input-otp-separator/>
    <dui-input-otp-group class="*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl">
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
    </dui-input-otp-group>
</dui-input-otp>

Pattern

Use the pattern attribute to constrain the allowed characters with a regular expression — any input that would make the value fail to match is rejected. It defaults to digits only (^\d*$). Pair it with inputmode to control the on-screen keyboard.

<dui-input-otp max-length="6" pattern="^[a-zA-Z0-9]*$" inputmode="text"/>

Composition

For full control over the layout, compose the slots by hand with the dui-input-otp-group, dui-input-otp-slot, and dui-input-otp-separator Tag Helpers instead of letting groups generate them. Slots take their position automatically in document order.

<dui-input-otp max-length="6">
    <dui-input-otp-group>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
    </dui-input-otp-group>
    <dui-input-otp-separator/>
    <dui-input-otp-group>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
        <dui-input-otp-slot/>
    </dui-input-otp-group>
</dui-input-otp>

Model binding

The dui-input-otp Tag Helper has full support for ASP.NET Core model binding using the asp-for attribute. Bind it to a string and the whole code posts straight back to the property.

When using model binding, DuneUI will automatically wrap the input inside a Field with the correct label and description derived from data attributes. You can opt out of this behavior by setting the render-field attribute to false.

<dui-input-otp asp-for="OneTimePassword" groups="3,3"/>
public class ModelBindingModel
{
    [Display(
        Name = "One-time password",
        Description = "Enter the 6-digit code we sent to your phone."
    )]
    public string OneTimePassword { get; set; } = "123";
}

Validation

Validation works like any other model-bound field. Add validation attributes to your model and the slots reflect the invalid state, with the error message rendered below.

<dui-input-otp asp-for="OneTimePassword" groups="3,3"/>
public class ValidationModel
{
    [Display(
        Name = "One-time password",
        Description = "Enter the 6-digit code we sent to your phone."
    )]
    [Required(ErrorMessage = "Enter the complete 6-digit code")]
    [StringLength(6, MinimumLength = 6, ErrorMessage = "Enter the complete 6-digit code")]
    public string OneTimePassword { get; set; } = "123";
}

Manual Validation

If you're not using ASP.NET Core model binding, you can manually indicate validation errors by setting the aria-invalid attribute to true. This applies the destructive styling to the slots.

The error message can be displayed using either a dui-field-error Tag Helper (when using explicit fields) or the error attribute (when using implicit fields).

<dui-field>
    <dui-label>One-time password</dui-label>
    <dui-input-otp aria-invalid="true" groups="3,3"/>
    <dui-field-error>Enter the complete 6-digit code</dui-field-error>
    <dui-field-description>
        Enter the 6-digit code we sent to your phone.
    </dui-field-description>
</dui-field>
<dui-input-otp
    label="One-time password"
    description="Enter the 6-digit code we sent to your phone."
    error="Enter the complete 6-digit code"
    aria-invalid="true"
    groups="3,3"/>

Disabled

Add the disabled attribute to prevent the user from interacting with the input.

<dui-input-otp max-length="6" value="123" disabled="true"/>

API Reference

<dui-input-otp>

The <dui-input-otp> tag helper renders a <del-input-otp> web component containing the slot cells and a single transparent <input> element that holds the whole code and posts it as one form value. In addition to the attributes below, it forwards any standard global attributes to the rendered container. When asp-for is set, it delegates to the built-in ASP.NET Core Input Tag Helper to resolve the input's name, value, and validation attributes.

Prop

Type

<dui-input-otp-group>

The <dui-input-otp-group> tag helper renders a <div> element that groups a run of slots. It has no attributes of its own and forwards any standard global attributes to the rendered element.

<dui-input-otp-slot>

The <dui-input-otp-slot> tag helper renders a <div> element that displays one character of the code and the active caret. In addition to the attribute below, it forwards any standard global attributes to the rendered element.

Prop

Type

<dui-input-otp-separator>

The <dui-input-otp-separator> tag helper renders a <div> element with role="separator", containing a minus icon by default — supply child content to override it. It has no attributes of its own and forwards any standard global attributes to the rendered element.

On this page