Skip to content
# OtpInput ## Description A one-time code entered as a row of single character boxes bound to one string. Focus advances as you type, the keyboard edits the code like a single field, and a pasted or SMS-filled code is cleaned up and spread over the boxes. It filters, folds and normalizes what it accepts, reports what it rejects, and carries the busy and error states of the round trip that follows. ## Parameters | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Accent | `BitColor?` | null | The color of the focused input's border and focus ring, and of the loading bar. The error state wins over it. | | AutoFocus | `bool` | false | Focuses the first empty input on the first render that finds the component enabled. | | AutoShift | `bool` | false | Shifts the rest of the code one input back when a character is cleared with Backspace or Delete, instead of leaving a hole. | | AutoSubmit | `bool` | false | Submits the enclosing form (a plain form or an EditForm) right after OnFill, the way pressing Enter would: the form still validates first. Nothing happens outside of a form. | | BlurOnFill | `bool` | false | Removes the focus from the inputs once the code is complete, which dismisses a phone's virtual keyboard. | | Classes | `BitOtpInputClassStyles?` | null | Custom CSS classes for different parts of the BitOtpInput. | | Description | `string?` | null | Helper text under the inputs, referenced by the group's aria-describedby. While Invalid or IsLoading is on it is also announced through a live region. | | DescriptionTemplate | `RenderFragment?` | null | Custom template for the helper text, taking precedence over Description. It is described the same way but never copied into the live region. | | FullWidth | `bool` | false | Stretches the row across its container and shares the width evenly between the inputs. The height stays the one of the Size. | | InputAriaLabelFormat | `string?` | null | Composite format of each input's aria-label: {0} is the one based position and {1} the Length. Defaults to "{0} of {1}". | | InputMode | `BitInputMode?` | null | The inputmode attribute of the inputs, which picks the virtual keyboard without changing the accepted characters. Defaults to the one the Type implies. | | Invalid | `bool` | false | Paints the error state and sets aria-invalid without an EditContext, e.g. for a code the server rejected. A failing validation shows the same state on its own. | | IsLoading | `bool` | false | The busy state of a submitted code: draws a progress bar, marks the group aria-busy, announces the Description and holds the code still like ReadOnly. Clear is not blocked by it. | | Label | `string?` | null | Label displayed above the inputs, bound to the first input and naming the group of inputs. | | LabelTemplate | `RenderFragment?` | null | Custom template for the label displayed above the inputs, taking precedence over Label. | | Length | `int` | 5 | The number of inputs, which is the length of the code. Values below 1 are treated as 1. | | Lowercase | `bool` | false | Converts every character to lower case before the Pattern is applied. Uppercase wins when both are set. | | Mask | `string?` | null | Text shown in place of every filled input's character. The value stays the typed code, and a masked code is kept off the clipboard. | | Merged | `bool` | false | Glues the inputs of each group (the ones the Separator makes) into a single field with rounding only at its ends. | | NormalizeDigits | `bool` | false | Converts the digits of other numbering systems (Persian, Arabic-Indic, full width, ...) to ASCII before the Type and the Pattern are applied. | | NoSmsAutoFill | `bool` | false | Turns off the WebOTP SMS auto fill, the one-time-code autocomplete and the password managers' autofill. | | OnFill | `EventCallback<string?>` | | Callback for when all of the inputs are filled, raised once per completed code. | | OnFocusIn | `EventCallback<(FocusEventArgs Event, int Index)>` | | onfocusin event callback for each input, with the index of the input. | | OnFocusOut | `EventCallback<(FocusEventArgs Event, int Index)>` | | onfocusout event callback for each input, with the index of the input. | | OnInput | `EventCallback<(ChangeEventArgs Event, int Index)>` | | oninput event callback for each input, with the index of the input. | | OnInvalid | `EventCallback<(string Value, int Index)>` | | Callback for when a keystroke, paste or auto fill is rejected in full by the Type, the Pattern or the PasteTransformer, with the rejected text and the index of the input. A paste that only loses some characters does not raise it. | | OnKeyDown | `EventCallback<(KeyboardEventArgs Event, int Index)>` | | onkeydown event callback for each input, with the index of the input. | | OnPaste | `EventCallback<(ClipboardEventArgs Event, int Index)>` | | onpaste event callback for each input, with the index of the input. | | PasteTransformer | `Func<string, string>?` | null | Applied to a pasted or auto filled chunk before it is filtered, e.g. to pull the code out of the message around it. An empty result rejects the chunk; an exception leaves it untouched. Not applied to a single typed character. | | Pattern | `string?` | null | A regular expression every single character has to match. Non-matching characters are rejected when typed and dropped when pasted; an invalid expression is ignored. | | Placeholder | `string?` | null | Hint text of the empty inputs. A string exactly Length long is spread one character per input; any other is shown in every input. | | Reversed | `bool` | false | Renders the inputs in the opposite order. The arrow keys follow. | | Separator | `string?` | null | Text rendered between the groups of inputs. It is hidden from assistive technologies and never part of the value. | | SeparatorInterval | `int` | 1 | The number of inputs in each group the Separator is rendered between, e.g. 3 for 123-456. Values below 1 are treated as 1. | | SeparatorTemplate | `RenderFragment<int>?` | null | Custom template rendered in place of the Separator text, with the zero based index of the next input as its context. | | Sequential | `bool` | false | Keeps the code free of holes: focusing, or pasting into, an input past the first empty one lands on that first empty input instead. A complete code is left editable anywhere. | | SingleTabStop | `bool` | false | Makes the whole component a single tab stop: only the first input is reachable with Tab. | | Size | `BitSize?` | null | The size of the inputs. | | Styles | `BitOtpInputClassStyles?` | null | Custom CSS styles for different parts of the BitOtpInput. | | Type | `BitInputType?` | null | Type of the inputs, deciding the accepted characters and the virtual keyboard. Number accepts digits only; Number, Email and Url render as text inputs. | | Uppercase | `bool` | false | Converts every character to upper case before the Pattern is applied. | | Variant | `BitVariant?` | null | The visual variant of the inputs: Outline (default), Fill or Text (underline only). | | Vertical | `bool` | false | Renders the inputs vertically. The arrow keys follow. | | DefaultValue | `TValue?` | null | The default value of the input to be used in uncontrolled mode (i.e. when the Value is not bound), typically used alongside the OnChange callback. | | DisplayName | `string?` | null | Gets or sets the display name for this field. | | InputHtmlAttributes | `IReadOnlyDictionary<string, object>?` | null | Gets or sets a collection of additional attributes that will be applied to the created element. | | Name | `string?` | null | Gets or sets the name of the element. Allows access by name from the associated form. | | NoValidate | `bool` | false | Disables the validation of the input. | | OnChange | `EventCallback<TValue?>` | | Callback for when the input value changes. | | ReadOnly | `bool` | false | Makes the input read-only. | | Required | `bool` | false | Makes the input required. | | Value | `TValue?` | null | Gets or sets the value of the input. This should be used with two-way binding. | | AriaLabel | `string?` | null | Gets or sets the accessible label for the component, used by assistive technologies. | | Class | `string?` | null | Gets or sets the CSS class name(s) to apply to the rendered element. | | Dir | `BitDir?` | null | Gets or sets the text directionality for the component's content. | | ForceAnimation | `bool` | false | Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested. | | HtmlAttributes | `Dictionary<string, object>` | new Dictionary<string, object>() | Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. | | Id | `string?` | null | Gets or sets the unique identifier for the component's root element. | | IsEnabled | `bool` | true | Gets or sets a value indicating whether the component is enabled and can respond to user interaction. | | Style | `string?` | null | Gets or sets the CSS style string to apply to the rendered element. | | TabIndex | `string?` | null | Gets or sets the tab order index for the component when navigating with the keyboard. | | Visibility | `BitVisibility` | BitVisibility.Visible | Gets or sets the visibility state (visible, hidden, or collapsed) of the component. | ## Public Members | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | InputElements | `ElementReference[]` | | The ElementReferences to the input elements of the BitOtpInput. The inherited InputElement is the first of them. | | BlurAsync | `() => ValueTask` | | Removes the focus from the input that holds it, dismissing a phone's virtual keyboard. Does nothing when the focus is elsewhere on the page. | | Clear | `() => Task` | | Clears all of the inputs and the value. Does nothing while the component is disabled or read-only. | | FocusAsync | `(int index = 0) => ValueTask` | | Focuses the input at the given index, clamped into range. The inherited FocusAsync() and FocusAsync(bool preventScroll) focus the first input. Does nothing before the first render. | | InputElement | `ElementReference` | | The ElementReference of the input element. | | FocusAsync() | `() => ValueTask` | | Gives focus to the input element. | | FocusAsync(bool preventScroll) | `(bool preventScroll) => ValueTask` | | Gives focus to the input element. | | UniqueId | `Guid` | Guid.NewGuid() | Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed. | | RootElement | `ElementReference` | | Gets the reference to the root HTML element associated with this component. | ## Enums ### BitSize Enum | Name | Value | Description | | :--- | :--- | :---------- | | Small | 0 | The small size. | | Medium | 1 | The medium size. | | Large | 2 | The large size. | ### BitInputType Enum | Name | Value | Description | | :--- | :--- | :---------- | | Text | 0 | The input expects text characters. | | Password | 1 | The input expects password characters. | | Number | 2 | The input expects number characters. | | Email | 3 | The input expects email characters. | | Tel | 4 | The input expects tel characters. | | Url | 5 | The input expects url characters. | ### BitInputMode Enum | Name | Value | Description | | :--- | :--- | :---------- | | None | 0 | No virtual keyboard. For when the page implements its own keyboard input control. | | Text | 1 | Standard input keyboard for the user's current locale. | | Decimal | 2 | Fractional numeric input keyboard containing the digits and decimal separator for the user's locale. | | Numeric | 3 | Numeric input keyboard, but only requires the digits 0–9. | | Tel | 4 | A telephone keypad input, including the digits 0–9, the asterisk (*), and the pound (#) key. | | Search | 5 | A virtual keyboard optimized for search input. | | Email | 6 | A virtual keyboard optimized for entering email addresses. | | Url | 7 | A keypad optimized for entering URLs. | ### BitVariant Enum | Name | Value | Description | | :--- | :--- | :---------- | | Fill | 0 | Fill styled variant. | | Outline | 1 | Outline styled variant. | | Text | 2 | Text styled variant. | ### BitColor Enum | Name | Value | Description | | :--- | :--- | :---------- | | Primary | 0 | Primary general color. | | Secondary | 1 | Secondary general color. | | Tertiary | 2 | Tertiary general color. | | Info | 3 | Info general color. | | Success | 4 | Success general color. | | Warning | 5 | Warning general color. | | SevereWarning | 6 | SevereWarning general color. | | Error | 7 | Error general color. | | PrimaryBackground | 8 | Primary background color. | | SecondaryBackground | 9 | Secondary background color. | | TertiaryBackground | 10 | Tertiary background color. | | PrimaryForeground | 11 | Primary foreground color. | | SecondaryForeground | 12 | Secondary foreground color. | | TertiaryForeground | 13 | Tertiary foreground color. | | PrimaryBorder | 14 | Primary border color. | | SecondaryBorder | 15 | Secondary border color. | | TertiaryBorder | 16 | Tertiary border color. | ### BitVisibility Enum | Name | Value | Description | | :--- | :--- | :---------- | | Visible | 0 | The content of the component is visible. | | Hidden | 1 | The content of the component is hidden, but the space it takes on the page remains (visibility:hidden). | | Collapsed | 2 | The component is hidden (display:none). | ### BitDir Enum | Name | Value | Description | | :--- | :--- | :---------- | | Ltr | 0 | Ltr (left to right) is to be used for languages that are written from the left to the right (like English). | | Rtl | 1 | Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic). | | Auto | 2 | Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element. | ## Sub Classes ### BitOtpInputClassStyles Properties | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Root | `string?` | null | Custom CSS classes/styles for the root element of the otp input. | | Label | `string?` | null | Custom CSS classes/styles for the label of the otp input. | | Description | `string?` | null | Custom CSS classes/styles for the description (helper text) of the otp input. | | InputsWrapper | `string?` | null | Custom CSS classes/styles for the wrapper element of the inputs. | | Input | `string?` | null | Custom CSS classes/styles for each input in otp input. | | Focused | `string?` | null | Custom CSS classes/styles for the focused input in otp input. | | Filled | `string?` | null | Custom CSS classes/styles for each input that already holds a character in otp input. | | Separator | `string?` | null | Custom CSS classes/styles for the separator rendered between the inputs of the otp input. | | Loader | `string?` | null | Custom CSS classes/styles for the progress bar rendered under the inputs while the otp input is in the loading state. | ## CSS Variables Public CSS custom properties read off the component's root. Set one on `:root` to restyle every instance, on any ancestor to restyle the ones inside it, or on the `Style` of one instance. | Name | Default Value | Description | | :--- | :------------ | :---------- | | `--bit-OtpInput-gap` | 0.625rem | Room between the inputs. Merged closes it to 0 whatever this holds. | | `--bit-OtpInput-input-size` | --bit-siz-ctrl-sm / --bit-siz-ctrl-md / --bit-siz-ctrl-lg, per Size | Width and height of every input, which is a square of the control height of its size class. FullWidth overrides the width alone. | | `--bit-OtpInput-input-width` | --bit-OtpInput-input-size | Width of an input on its own, for a box wider than it is tall. | | `--bit-OtpInput-input-height` | --bit-OtpInput-input-size | Height of an input on its own. | | `--bit-OtpInput-font-family` | --bit-tpg-font-family | Typeface of the whole component, which is where a tabular or monospaced face for the code is set - the one piece of text in a form that is read character by character. | | `--bit-OtpInput-font-size` | --bit-tpg-fs-xs / --bit-tpg-fs-sm / --bit-tpg-fs-md, per Size | Size of the code, inherited by the label and the placeholder. | | `--bit-OtpInput-label-font-size` | --bit-OtpInput-font-size | Size of the label above the inputs, which follows the size of the code unless it is set on its own. | | `--bit-OtpInput-label-font-weight` | --bit-tpg-fw-semibold | Weight of the label above the inputs. | | `--bit-OtpInput-description-font-size` | --bit-tpg-fs-2xs / --bit-tpg-fs-xs / --bit-tpg-fs-sm, per Size | Size of the helper text under the inputs, one step of the type ramp below the code. | | `--bit-OtpInput-font-weight` | --bit-tpg-fw-regular | Weight of the character inside an input. | | `--bit-OtpInput-radius` | --bit-shp-radius-control | Corner radius of an input, and of the two ends of every group while Merged is on. The Text variant squares them off whatever this holds. | | `--bit-OtpInput-border-width` | --bit-shp-border-width | Thickness of an input's rule, and with it the overlap that glues two Merged inputs together. | | `--bit-OtpInput-color` | --bit-clr-fg-pri | Color of the typed character. | | `--bit-OtpInput-background` | Per Variant: --bit-clr-bg-pri (Outline), --bit-clr-bg-sec (Fill), transparent (Text) | Input background at rest, and the fallback of the hover and filled backgrounds below. | | `--bit-OtpInput-hover-background` | Per Variant: the rest background (Outline, Text), --bit-clr-bg-sec-hover (Fill) | Input background while hovered, on an input that is neither disabled nor read-only. | | `--bit-OtpInput-border-color` | Per Variant: --bit-clr-brd-pri (Outline, Text), transparent (Fill) | Input rule at rest, and the fallback of the hover and filled rules below. | | `--bit-OtpInput-hover-border-color` | Per Variant: --bit-clr-brd-pri-hover (Outline, Text), the rest rule (Fill) | Input rule while hovered. | | `--bit-OtpInput-filled-background` | --bit-OtpInput-background | Background of an input that already holds a character, which is what turns the row into its own progress indicator. It has no parameter behind it. | | `--bit-OtpInput-filled-border-color` | --bit-OtpInput-border-color | Rule of an input that already holds a character. | | `--bit-OtpInput-focus-border-color` | The Accent role's main color | Input rule while focused. | | `--bit-OtpInput-focus-color` | The Accent role's focus color | Color of the keyboard focus ring. | | `--bit-OtpInput-placeholder-color` | --bit-clr-fg-ter | Hint character of an empty input. | | `--bit-OtpInput-label-color` | --bit-clr-fg-pri | Label above the inputs. | | `--bit-OtpInput-description-color` | --bit-clr-fg-sec | Helper text under the inputs, outside the error state. | | `--bit-OtpInput-separator-color` | --bit-clr-fg-sec | Text drawn between the groups of the code. | | `--bit-OtpInput-invalid-color` | --bit-clr-err | Input rule, helper text and loading bar while Invalid is on or a validation is failing. | | `--bit-OtpInput-invalid-focus-color` | --bit-clr-err-focus | Focus ring color in the error state. | | `--bit-OtpInput-disabled-color` | --bit-clr-fg-dis | Character, placeholder, label, helper text, separator and loading bar when IsEnabled is false. | | `--bit-OtpInput-disabled-background` | --bit-clr-bg-dis | Input background when IsEnabled is false. | | `--bit-OtpInput-disabled-border-color` | --bit-clr-brd-dis | Input rule when IsEnabled is false. | | `--bit-OtpInput-loader-color` | The Accent role's main color | The sweep of the bar drawn while IsLoading is on. The error and disabled states paint it with their own color instead. | | `--bit-OtpInput-loader-background` | --bit-clr-bg-sec | The track the sweep of the loading bar travels along. | | `--bit-OtpInput-loader-height` | --bit-siz-track-sm | Thickness of the loading bar. | ## Examples \n**Basic**: ```razor ``` \n**Label & description**: ```razor Custom label Didn't get it? Send it again ``` \n**Type & keyboard**: ```razor
Value: @normalizeDigitsValue
``` ```csharp private string? normalizeDigitsValue; ``` \n**Mask & placeholder**: ```razor
Value: @maskValue
``` ```csharp private string? maskValue; ``` \n**Pattern & casing**: ```razor ``` \n**Separator**: ```razor ``` \n**Variant**: ```razor ``` \n**Layout**: ```razor
``` \n**Merged**: ```razor ``` \n**Autofill, paste & copy**: ```razor
Value: @pasteValue
Value: @transformedPasteValue
``` ```csharp private string? pasteValue; private string? transformedPasteValue; ``` \n**Binding**: ```razor ``` ```csharp private string? oneWayValue; private string? twoWayValue; ``` \n**Events**: ```razor
OnChange value: @onChangeValue
OnFill value: @onFillValue
Rejected: @onInvalidArgs?.Value
Input index: @onInvalidArgs?.Index
Focus type: @onFocusInArgs?.Event.Type
Input index: @onFocusInArgs?.Index
Focus type: @onFocusOutArgs?.Event.Type
Input index: @onFocusOutArgs?.Index
Value: @onInputArgs?.Event.Value
Input index: @onInputArgs?.Index
Key & Code: [@onKeyDownArgs?.Event.Key] [@onKeyDownArgs?.Event.Code]
Input index: @onKeyDownArgs?.Index
Focus type: @onPasteArgs?.Event.Type
Input index: @onPasteArgs?.Index
``` ```csharp private string? onChangeValue; private string? onFillValue; private (string Value, int Index)? onInvalidArgs; private (FocusEventArgs Event, int Index)? onFocusInArgs; private (FocusEventArgs Event, int Index)? onFocusOutArgs; private (ChangeEventArgs Event, int Index)? onInputArgs; private (KeyboardEventArgs Event, int Index)? onKeyDownArgs; private (ClipboardEventArgs Event, int Index)? onPasteArgs; ``` \n**Public API**: ```razor Focus first Focus last Blur Clear ``` ```csharp private BitOtpInput? apiOtpInput; private async Task HandleClearClick() { if (apiOtpInput is null) return; await apiOtpInput.Clear(); await apiOtpInput.FocusAsync(); } ``` \n**Validation & auto submit**: ```razor @if (formIsValidSubmit is false) { Submit } else { The form submitted successfully. } @if (autoSubmitted is false) { } else { Submitted on fill: @autoSubmitOtpInputModel.OtpValue } ``` ```csharp public class ValidationOtpInputModel { [Required(ErrorMessage = "The OTP value is required.")] [MinLength(6, ErrorMessage = "Minimum length is 6.")] public string OtpValue { get; set; } } private bool formIsValidSubmit; private ValidationOtpInputModel validationOtpInputModel = new(); private void HandleValidSubmit() { formIsValidSubmit = true; } private void HandleInvalidSubmit() { formIsValidSubmit = false; } private bool autoSubmitted; private ValidationOtpInputModel autoSubmitOtpInputModel = new(); private void HandleAutoSubmit() { autoSubmitted = true; } ``` \n**Loading & invalid**: ```razor Clear & retry ``` ```csharp private bool isLoading; private bool loadingInvalid; private BitOtpInput? loadingOtpInput; private string loadingDescription = "Enter the 6 digit code we sent you. Try 123456."; private async Task HandleLoadingDemoFill(string? value) { isLoading = true; loadingInvalid = false; loadingDescription = "Checking the code…"; await Task.Delay(2000); // the server checking the code isLoading = false; loadingInvalid = value != "123456"; loadingDescription = loadingInvalid ? "That code is not correct or has expired. Try 123456." : "That code is correct."; } private async Task HandleLoadingDemoRetry() { isLoading = false; loadingInvalid = false; loadingDescription = "Enter the 6 digit code we sent you. Try 123456."; if (loadingOtpInput is null) return; await loadingOtpInput.Clear(); await loadingOtpInput.FocusAsync(); } ``` \n**Accessibility**: ```razor ``` \n**Cascading parameters**: ```razor ``` ```csharp private readonly BitOtpInputParams[] otpInputParams = [ new() { Length = 6, Separator = "-", SeparatorInterval = 3, Type = BitInputType.Number, Variant = BitVariant.Fill, NormalizeDigits = true, PasteTransformer = v => System.Text.RegularExpressions.Regex.Match(v, @"\p{Nd}{6}").Value, } ]; ``` \n**Accent**: ```razor ``` \n**Size**: ```razor ``` \n**Style & Class**: ```razor
``` \n**RTL**: ```razor ```