Skip to content

Inputs

OtpInput

Bit.BlazorUI

The OTP input collects a one-time passcode, the short numeric or alphanumeric code that a multi factor authentication flow sends over SMS or email, as a row of single character boxes bound to one string value. Typing a character advances the focus to the next box, Backspace and Delete take the code apart character by character, the arrow keys along with Home and End move between the boxes, pasting a whole code cleans it up and spreads it over them (a transformer of your own can even pull the code out of the message it was copied inside of), copying or cutting from any box hands over the whole code rather than the one character it holds, and on supporting browsers the code that just arrived by SMS is filled in automatically. The set of characters it accepts is narrowed down by the input type or by a regular expression, upper or lower cased on the way in, and normalized to ASCII when it arrives written in another numbering system; what is rejected outright is reported rather than silently dropped. The code is masked with a character of your own choosing, split into groups by a separator, laid out horizontally or vertically in either direction, kept free of holes, and the whole row can be turned into a single stop of the tab order. The boxes form a labelled and described accessible group, each announcing its own position. It completes with a callback to submit from, takes part in an EditForm like any other input, shows the wait while the code is being checked, and paints the error state of a code that the server sent back as wrong.

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

The component renders Length single character boxes (5 by default) and keeps their content in a single string value. The focus moves on by itself as each box is filled, so the whole code is typed without ever reaching for the Tab key.

The keyboard follows the same rules a single input holding the whole code would: Backspace clears the box it is pressed in and stays there, so the character can be retyped right away, and only once that box is empty does it delete the character before it and follow it. Delete clears the current box without moving. The arrow keys walk between the boxes, and Home and End jump to the first and the last character of the code.

The mouse is treated the same way: focusing a box selects the character it holds, so typing replaces it instead of appending to it, and a click that lands in the gap between two boxes, or on a separator, is not swallowed either - it puts the caret in the box the typing is meant to carry on in, which is the first one still empty, or the last one once the code is complete. The whole row therefore behaves like the single field it represents rather than like a handful of unrelated boxes standing next to each other.

IsEnabled and ReadOnly are the two ways to stop the user from editing the code: a disabled component is dimmed and drops out of the tab order, while a read-only one still reads and copies like normal text, which is what an already confirmed code should look like. AutoFocus puts the caret in the first box still left to fill on the first render, so a component seeded with a partial code carries on where the typing stopped instead of landing back on its first character. AutoShift makes Backspace and Delete pull the remaining characters one box to the left instead of leaving a hole in the middle of the code, and BlurOnFill drops the focus as soon as the last box is filled, which is what gets the virtual keyboard of a phone out of the way of the button below the code.

Sequential keeps the code free of holes: clicking a box that sits after the first empty one moves the focus to that empty box instead, and a pasted chunk cannot land past it either, so the code is always filled from its start onwards. It matters because the value is the characters of the boxes joined together and an empty box contributes nothing to it, so a character typed into the middle of an empty row would be reported as if it were the first one of the code. A complete code is left alone, which keeps every one of its characters clickable and correctable.


Basic




Length = 4




Disabled




ReadOnly




AutoFocus




AutoShift




BlurOnFill




Sequential (try clicking the last box)

Label & description

Label renders a caption above the boxes. It is a real label element bound to the first box, so clicking it moves the focus there, and the group of inputs is named after it for screen readers. LabelTemplate replaces the text with arbitrary markup while keeping the same wiring, and Required marks the label with an asterisk and the boxes as required for the browser and for assistive technologies.

Description is the helper text under the boxes, and it is where the sentence that turns a row of empty boxes into a question the user can answer belongs: where the code was sent, how long it stays valid, or what to do when it never arrives. The group of boxes references it through aria-describedby, so a screen reader announces it along with the name of the group instead of leaving it as text that only sighted users get. It is deliberately attached to the group and not to each box, which would otherwise repeat the whole sentence at every character of the code. DescriptionTemplate replaces it with arbitrary markup, so a "resend the code" action or a countdown put in there is announced with the group just the same.


Label:






Required:






LabelTemplate:






Description:


We sent a 6 digit code to +1 555 0100. It stays valid for 10 minutes.




DescriptionTemplate:


Didn't get it? Send it again

Type & keyboard

Type decides the kind of the underlying inputs and, with it, the virtual keyboard that the mobile browsers bring up. Number asks for the numeric keypad and rejects anything that is not a digit, whether it is typed or pasted, so a code copied as 123-456 still lands in the boxes as 123456. It is deliberately not rendered as a native number input, which would carry spin buttons, react to the mouse wheel and silently report an empty value for the characters a number accepts but a code does not, such as e or -. Password masks the characters with the bullet of the browser, which is what a code that stays on screen for a while should do.

The same reasoning applies to Email and Url: those input types carry a constraint validation that a single character can never satisfy, so a row of them would report itself as permanently invalid to the browser and keep a plain HTML form from ever submitting. They are rendered as text inputs as well and contribute only their keyboard, through the inputmode. Tel is the exception and is rendered as it is, since it validates nothing.

InputMode asks for a keyboard on its own, without changing the element that is rendered or the characters that are accepted. It defaults to the keyboard that matches the Type, so it is only needed to ask for one the type does not imply: Tel on a code of digits brings up the telephone keypad, whose keys are noticeably larger than the numeric ones on most Android keyboards, which makes a code easier to hit on a small screen.

A code of digits is not always written in the digits of ASCII: a message in Persian carries it as ۱۲۳۴۵۶, one in Arabic as ١٢٣٤٥٦, and a keyboard set to those languages types them that way. NormalizeDigits turns the digits of every other numbering system into their ASCII form as they are typed or pasted, which is what keeps such a code from being refused as if it were not a number at all. The conversion happens before the Type and the Pattern decide what to accept, and the value of the component is the ASCII form that a server expects.









Value:
Try pasting ۱۲۳۴۵۶ or ١٢٣٤٥٦.

Mask

Mask is the text that every filled box shows in place of the character it holds, which hides the code without turning the boxes into password inputs, so the masking character is chosen rather than dictated by the browser: a bullet, an asterisk, a star, even an emoji. Only the rendering changes; the value of the component stays the code that was typed, and it is what binding, validation and the callbacks all see.

A masked code is deliberately kept off the clipboard: copying from a box hands over the whole code everywhere else, but here the boxes are showing a masking character rather than the code, and a Password Type, which browsers refuse to let anyone copy from, is treated the same way. Pasting into the boxes keeps working either way.







Value:

Pattern

Pattern is a regular expression that every single character of the code has to match, which narrows the code down to a set of characters that no input type covers on its own. A character that does not match is rejected as it is typed, and the characters of a pasted code that do not match are dropped instead of the whole paste being refused, so a code copied together with the words around it still lands in the boxes. An expression that does not compile is ignored rather than turning the component into a field that accepts nothing.

Uppercase is what keeps such a restriction from becoming a dead end: it turns every character into its upper case form before the expression is applied, so a code that is printed in upper case can be typed in either case, on a phone keyboard as much as on a desk one. Lowercase is its mirror and follows exactly the same rules; Uppercase wins when both are set, since asking for both at once is a contradiction rather than an order to apply. Both of them reach a code that is assigned to the component as well as one that is typed or pasted into it, so the boxes never show one casing while the value reports another.







Placeholder

Placeholder puts a hint character in the boxes that are still empty, which makes the expected shape of the code visible before anything is typed. A placeholder exactly as long as Length is spread over the boxes one character each, so a mask like 000000 or ABCDEF can be shown; any other value is repeated in every box as is.



Separator

Separator draws a piece of text between the boxes, the way a long code is usually printed in the message that delivers it. It is purely decorative: it is hidden from screen readers, it does not take the focus, and it is never rendered as a part of the value. A pasted code is filtered by the same rules as a typed one, so the separators of a code copied with them in it are dropped when Type is BitInputType.Number or a Pattern that rejects them is set, and are taken as characters of the code otherwise.

SeparatorInterval decides how many boxes each group holds, which is what splits a long code into the chunks it is read in. It defaults to 1, a separator between every pair of boxes; setting it to 3 on a six character code renders a single separator in the middle and turns the row into 123-456.

SeparatorTemplate replaces that text with arbitrary markup, an icon above all, and takes precedence over Separator. Its context is the zero based index of the box the separator is rendered before, so a single template can tell one separator of the row from another and render something different at each of them.









Directions

Vertical stacks the boxes in a column and Reversed renders them in the opposite order. The arrow keys follow whatever layout is in effect: left and right walk a horizontal row, up and down walk a vertical one, and both flip along with Reversed and with a right-to-left direction, so the focus always moves the way the boxes look. Home and End are the exception on purpose: they address the code rather than the layout, so they always land on the first and the last character of it.







Merged

Merged glues the boxes together into a single field instead of leaving them standing next to each other: the gaps between them are closed, the rule that two neighbouring boxes share is drawn once rather than twice, and only the two ends of the row keep their rounding. What it draws is a code printed in one box, which is how a code is printed on the card or in the message it comes from, while every character keeps the input of its own that the typing, the autofill and the screen readers need.

A Separator cuts the row into groups and the gluing follows those groups, so a six character code with a SeparatorInterval of 3 is drawn as two joined boxes of three with the separator between them. The separator keeps a little room of its own there, since closing the gaps would otherwise leave a dash touching the boxes on either side of it. A group of a single box keeps all four of its corners rounded, which is what a separator between every pair of boxes falls back to.

It composes with everything else about the layout: Vertical glues the boxes along the column instead of the row, Reversed and a right-to-left direction keep the rounding at the two visual ends of each group, and the Variant decides what is being joined - a row of filled boxes, of outlined ones, or a single continuous underline. The focused box is drawn above the ones it is glued to, so its ring is never clipped by them.











Accessibility

The boxes are wrapped in a group that is named after the Label, or after AriaLabel when there is none, so a screen reader announces the purpose of the code once instead of once per box. Each box is then named after its own position, 1 of 6 by default, which is what tells the user which character they are on. InputAriaLabelFormat is the composite format behind that name, where {0} is the one based position and {1} is the Length, and it is what localizes the announcement.

Description completes that picture: the group also carries an aria-describedby pointing at the helper text under the boxes, so the sentence that says where the code was sent and how long it is good for is announced along with the name of the group rather than being left as text that only sighted users get.

SingleTabStop turns the whole component into one stop of the tab order: only the box holding the first character stays reachable with the Tab key and the rest are left to the auto advancing focus, the arrow keys and the mouse. A keyboard user then tabs past the code in one press instead of six, the way a group of inputs that hold one single value is expected to behave. The boxes still take the focus on a click and through FocusAsync, so nothing else about them changes.

TabIndex places the component itself somewhere in the tab order and is rendered on each box, which is what puts a code entry ahead of the fields printed above it on a page whose reading order and DOM order disagree. It composes with SingleTabStop rather than fighting it: a box that was taken out of the tab order has no position left to be given, so only the reachable one carries it. Use it sparingly, since a positive value pulls the component out of the natural order of the document for every user, not only for the one it was meant to help.

The rest of the accessible behavior comes for free: Required marks every box as required for the browser and for assistive technologies, a failing validation and the Invalid state add aria-invalid to them, IsLoading marks the group with aria-busy so that the wait for the answer of the server is announced rather than only drawn, the separators are hidden from screen readers so the code is never read out with the dashes in it, and a disabled component drops out of the tab order altogether.









Enter the code from the text message we sent to +1 555 0100.

Autofill, paste & copy

A one-time code is copied far more often than it is typed, so the whole code arrives in one go in three different ways and all of them end up in the same place. Every box carries autocomplete="one-time-code", which is what makes iOS and Safari offer the code that has just arrived by SMS; on browsers that implement the WebOTP API the component asks the browser for that code itself and fills the boxes as soon as the user allows it; and a paste anywhere in the row is caught and spread over the boxes. NoSmsAutoFill turns the first two off for a code that never arrives by SMS, such as one read from an authenticator app.

A pasted code is cleaned up before it lands: every kind of whitespace is dropped, so a code that wraps or is spaced in the message it was copied from still fills the boxes, and so are the characters that the Type or the Pattern rejects, which is what lets a code copied as 123-456 or together with the words around it be pasted as is instead of being refused. Uppercase applies to it too.

The characters that carry no glyph at all are dropped whatever the Type and the Pattern say, since no code is ever made of them: the bidi marks that a message written in Persian or Arabic carries around its digits, the zero width joiners of the scripts that need them, the byte order mark that a few clipboards prepend, and the control characters. They are invisible, so letting them through would fill the boxes with characters the user cannot see and hand the server a code it never issued - a code that looks right on screen and is refused every single time.

Where the code lands depends on how much of it there is: one that fills the component always starts at the first box, no matter which one received the paste, since anything else would drop its leading characters, while a shorter one is inserted where it was pasted and the focus moves to the first box still left to fill. A partial paste only replaces as many characters as it brings, so the rest of the code around it is left alone, and a code longer than the boxes can hold keeps its first Length characters. Sequential applies here too: it keeps a pasted chunk from landing past the first empty box, which is the hole it exists to prevent.

That per-character filtering is enough for a code of digits, where everything around the code is thrown away by definition, but it cannot pull a code of letters out of the text it was copied inside of: the letters of the words match the code just as well as the code does, so your code is A1B2C3 would fill the boxes with YOURCODEI. PasteTransformer is the way out. It is a function applied to a chunk of characters that reaches the component in one go, before anything else is done with it, so a single regular expression picks the code out of the sentence. Returning an empty string rejects the whole chunk, which raises OnInvalid, and it is deliberately not applied to a single typed character.

The way back out is handled too. Each box is an input of its own, so copying from one of them would hand over the single character it holds instead of the code the user meant; the whole code is put on the clipboard instead, from whichever box the copy is made, which is what a single input holding the code would have given. Cutting does the same and empties the boxes afterwards. A code that is not shown is left alone: with a Mask or a Password type the boxes hold a masking character rather than the code, and a password input refuses to be copied for exactly the same reason.

NoSmsAutoFill reaches the password managers as well. An autocomplete of off is a request the browser extensions deliberately ignore, since it is what a site refusing to work with them looks like, so the attributes that 1Password, LastPass, Bitwarden and Dashlane read instead are rendered along with it. They are not rendered otherwise, because offering the code from the authenticator vault that issued it is precisely what a password manager is for.

Try pasting 123 456, 12-34-56 or your code is 123456:

Value:


Try pasting your code is A1B2C3, do not share it:

Value:


Binding

The value of the component is the whole code as a single string, not one value per box. Binding it one-way with Value makes the boxes follow the model without letting the user change it, while @bind-Value keeps the two in sync in both directions: typing in the boxes updates the string, and writing a new string into the model spreads it over the boxes. Setting the value to null or to an empty string clears them all. DefaultValue is the uncontrolled counterpart: it seeds the boxes on the first render and then leaves them to the user.



Events

OnChange reports every change of the value, while OnFill fires the moment the last empty box is filled, which is the callback to submit the code from - and, with it, the place to switch IsLoading on while the answer of the server is awaited. It is raised once per completed code, so retyping the same character over an already complete code does not submit it twice, and it fires again after the code is edited into a different complete one.

OnInvalid is the other side of that coin: it fires when what was typed, pasted or auto filled is rejected in full by the Type or the Pattern, so that nothing of it reaches the boxes, and it receives the rejected text along with the index of the box that received it. Rejecting a character is otherwise silent, which leaves the user typing into a box that never fills without being told why, so this is the callback that turns it into a message, a shake of the row or the Invalid state. A paste that only loses some of its characters, a code copied with the dashes in it, still fills the boxes and is therefore not a rejection.

The remaining callbacks forward the raw DOM events of the individual boxes together with the index of the box that raised them, which is what makes per-box behavior possible in the consuming code.

OnChange value:


OnFill value:


Rejected:
Input index:


Focus type:
Input index:


Focus type:
Input index:


Value:
Input index:


Key & Code: [] []
Input index:


Focus type:
Input index:

Public API

A reference to the component exposes FocusAsync, which focuses a specific box (the index is clamped into the rendered range), BlurAsync, which drops the focus of whichever box holds it and with it the virtual keyboard of a phone, Clear, which empties every box and the value at once, and InputElements, the element references of the boxes themselves. Clearing and re-focusing the first box is exactly what a "the code you entered is wrong, try again" branch needs to do.

All of them are safe to call at any point in the lifetime of the component: focusing before the first render does nothing rather than asking the browser for an element that is not there yet, blurring does nothing when the focus is elsewhere on the page, and clearing does nothing while the component is disabled or read-only. Clear is deliberately not blocked by IsLoading, since the busy state belongs to the very code that is about to be cleared and switched off again.


Validation

The component is a regular Blazor input, so it takes part in an EditForm like any other one: it picks up the cascading EditContext, reports its value on every keystroke, renders the error state on the boxes, and marks itself with aria-invalid for assistive technologies. Data annotations such as [Required] and [MinLength] on the bound property are all that is needed to validate the length of the code. Outside of an EditForm, Name posts the whole code as one named field of a plain HTML form, not one field per box.



Invalid

A one-time code is almost never wrong in a way a validator can see: it is the right length and it is made of the right characters, and only the server that issued it knows that it has expired or does not match. Invalid is what paints that answer back onto the boxes, without an EditContext taking any part in it. It draws them in the error color, keeps them there while the user reads the message, and marks them with aria-invalid so that the failure is announced rather than only shown.

It is independent of the validation of an EditForm, which renders the very same state on its own, so the two can be used together: data annotations catch a code that is too short before it is ever sent, and Invalid catches the one that was sent and came back rejected. Clearing the boxes and putting the caret back in the first of them, which is what Clear and FocusAsync do together, is the usual companion to it. The wait in between the two, while the code is on its way to the server, is what IsLoading is for.

Invalid paints the failure but does not say what it was, so it belongs together with Description, which carries the sentence the server answered with. Because the group of boxes references the description through aria-describedby, the reason is announced to a screen reader user rather than only being drawn in red next to boxes they cannot see, and a colour alone is never the only carrier of the message. A description that is referenced this way is announced when the focus reaches the code, though, not at the moment the server answers; when the answer arrives while the focus is elsewhere, put the sentence inside a DescriptionTemplate whose own markup carries an aria-live="polite", so that it is read out as it changes. It is deliberately not done for you, since a description holding a countdown or a "resend the code" link would then be announced on every tick.

Enter the 6 digit code we sent you. Try 123456.

Loading

Entering a one-time code is a round trip: the last box is filled, the code is sent, and only the answer of the server says whether it was the right one. IsLoading is the middle of that trip. It draws an indeterminate progress bar under the boxes, so the wait is visible where the code is rather than somewhere else on the page, and it marks the group of boxes with aria-busy, which is what tells a screen reader that the answer is still on its way instead of leaving the user wondering whether the code was received at all.

It also holds the code still while the answer travels, exactly the way ReadOnly does: nothing can be typed, deleted, pasted or cut over a code that has already been submitted, and the component stops asking the browser for the SMS code it is no longer waiting for. What it does not block is your own Clear, since the busy state belongs to you: clearing the boxes is precisely what the branch that turns IsLoading off and Invalid on is about to do.

The three states are meant to be used together and in this order: OnFill submits the code and switches IsLoading on, the answer switches it back off, and a code that came back rejected switches Invalid on along with the sentence the server answered with in the Description. The bar follows the Accent color, so it belongs to the same form as the boxes above it.

Enter the 6 digit code we sent you. Try 123456.

Variant

Variant decides how much of a frame each box carries: Outline (the default) draws a full rule around it, Fill paints it with a surface color and drops the rule, and Text keeps only an underline, for a code entry that should not outweigh the fields around it.





Accent

Accent picks the color role that the focused box carries, on both its border and its focus ring, so the component can follow the accent of the form it sits in. The error state of the validation always wins over it, which keeps an invalid code recognizable whatever the accent is.















Size

Size scales the boxes and the text inside them together, so the code stays centered and readable at every size. Pick the one that matches the density of the surrounding form: the large size suits a standalone verification screen, the small one a code entry tucked into a dense dialog.





Style & Class

Style and Class reach the root element only, while Styles and Classes reach every part of the component by name: the root, the label, the description, the wrapper of the inputs, each input, the separator, the Loader drawn while IsLoading is on, plus the two state slots Focused (the input that currently has the focus) and Filled (every input that already holds a character), which are the hooks for a per-box appearance that follows the progress of the typing.


Component's Style & Class:





Styles & Classes:

Every part of the component has a slot of its own.

Every part of the component has a slot of its own.

RTL

In a right-to-left direction the boxes are laid out from right to left and the arrow keys swap along with them, so the left arrow still moves towards the box that sits on the visual left, which is the one holding the next character. The value itself keeps its logical order: the first box is always the first character of the code.






API

Every parameter, public member, sub-class and enum this component exposes.

BitOtpInput parameters

Name Type Default value Description
Accent BitColor? null The accent color of the inputs, applied to the border and the focus ring of the focused input. The error state of the validation still wins over it.
AutoFocus bool false If true, the first input left to fill is auto focused on the first render, so a component seeded with a partial code carries on where the typing stopped. A component that starts out disabled cannot take the focus, so it is focused on the first render that finds it enabled instead of losing the auto focus altogether.
AutoShift bool false Enables auto shifting the indexes while clearing the inputs using Delete or Backspace, so the remaining characters move one input to the left instead of leaving a hole in the middle of the code.
BlurOnFill bool false Removes the focus from the inputs as soon as the code is complete, which is what dismisses the virtual keyboard of a phone once there is nothing left to type.
Classes BitOtpInputClassStyles? null Custom CSS classes for different parts of the BitOtpInput.
Description string? null The description (helper text) rendered under the inputs, which the group of the inputs references through its aria-describedby so that screen readers announce it along with the name of the group. It is where the sentence that turns a row of empty boxes into a question the user can answer belongs: where the code was sent, how long it is good for, or what a server that rejected it said.
DescriptionTemplate RenderFragment? null Custom template for the description (helper text) rendered under the inputs, which takes precedence over the Description. It is referenced the very same way, so a "resend the code" button or a countdown put in here is announced with the group as well.
InputAriaLabelFormat string? null The composite format of the aria-label rendered on each input, where {0} is the one based index of the input and {1} is the Length. Set it to localize the position that screen readers announce for each input. The default is "{0} of {1}".
InputMode BitInputMode? null Sets the inputmode html attribute of the inputs, which is what decides the virtual keyboard that a phone brings up without changing the element that is rendered or the characters that are accepted. It defaults to the keyboard that matches the Type, so it is only needed to ask for a keyboard the type does not imply, like the telephone keypad (whose keys are larger than the numeric ones on most Android keyboards) for a code of digits.
Invalid bool false Paints the inputs with the error state without an EditContext taking part in it, which is what reports a code that the server has rejected ("that code is not correct, try again"): the failure only becomes known once the code has been submitted, so there is nothing for a validator to see. It also marks the inputs with aria-invalid, and a failing validation of an EditContext still shows the very same state on its own.
IsLoading bool false Puts the component into the busy state of a code that has been submitted and is being checked, which is the step between the OnFill and the answer that either lets the user through or sets the Invalid. It paints an indeterminate progress bar under the inputs, marks the group with aria-busy so that the wait is announced rather than only shown, and holds the code still the way the ReadOnly does, so that nothing can be typed, pasted or cut over a code whose answer is already on its way. The Clear of the consumer is deliberately not blocked by it.
Label string? null Label displayed above the inputs. It is rendered as a real label element bound to the first input and it also names the group of the inputs for assistive technologies.
LabelTemplate RenderFragment? null Custom template for the label displayed above the inputs, taking precedence over Label.
Length int 5 Length of the OTP or number of the inputs. Values below 1 are clamped to 1, changing it at runtime keeps the characters of the inputs that survive the resize, and a value longer than the inputs can hold loses its extra characters instead of being reported as a value that is not shown.
Lowercase bool false Turns every character of the code into its lower case form as it is typed or pasted, the mirror of the Uppercase and applied under the very same rules: before the Pattern is applied, so an expression restricted to lower case letters accepts an upper case keystroke, and to a code that is assigned to the component as much as to one that is typed into it. The Uppercase wins when both are set.
Mask string? null The text rendered in place of every filled input, which hides the code without turning the inputs into password inputs, so a masking character of its own (a bullet, an asterisk, an emoji) can be used. The value of the component stays the code that was typed.
Merged bool false Glues the inputs of each group together into a single field instead of leaving them standing next to each other: the gaps between them are closed, the rule they share is drawn once, and only the two ends of every group keep their rounding, which is the look of a code printed in a single box. The groups are the ones the Separator makes, so a separator with a SeparatorInterval of 3 renders a six character code as two joined boxes of three.
NormalizeDigits bool false Turns the digits of the other numbering systems (the Persian ۰۱۲۳, the Arabic-Indic ٠١٢٣, the full width 0123 and the rest) into their ASCII form as they are typed or pasted, which is what lets a code that arrives in a message written in the language of the user be typed on the keyboard of that language rather than being rejected as if it were not a number at all. The conversion happens before the Pattern is applied and before the Type rejects what is not a digit, and the value of the component is the ASCII form that a server expects.
NoSmsAutoFill bool false Disables both the SMS auto fill of the OTP through the WebOTP API of the browser and the one-time-code autofill of the inputs themselves. It also renders the attributes that keep the password manager extensions (1Password, LastPass, Bitwarden, Dashlane) from filling the inputs and from putting their badge over them, since an autocomplete of "off" is a request those extensions deliberately ignore.
OnFill EventCallback<string?> Callback for when all of the inputs are filled. It is raised once per completed code, so an edit that keeps the very same code does not raise it again.
OnFocusIn EventCallback<(FocusEventArgs Event, int Index)> onfocusin event callback for each input, receiving the event and the index of the input that raised it.
OnFocusOut EventCallback<(FocusEventArgs Event, int Index)> onfocusout event callback for each input, receiving the event and the index of the input that raised it.
OnInput EventCallback<(ChangeEventArgs Event, int Index)> oninput event callback for each input, receiving the event and the index of the input that raised it.
OnInvalid EventCallback<(string Value, int Index)> Callback for when what was typed, pasted or auto filled is rejected in full by the Type or the Pattern, so that nothing of it reaches the inputs. It receives the rejected text along with the index of the input that received it, and it is what turns a silent rejection into a visible one. A paste that only loses some of its characters, like a code copied with the dashes in it, is not a rejection and does not raise it.
OnKeyDown EventCallback<(KeyboardEventArgs Event, int Index)> onkeydown event callback for each input, receiving the event and the index of the input that raised it.
OnPaste EventCallback<(ClipboardEventArgs Event, int Index)> onpaste event callback for each input, receiving the event and the index of the input that raised it.
PasteTransformer Func<string, string>? null A function applied to a chunk of characters that reaches the component in one go (a paste, an SMS auto fill, or a multi character input event) before anything else is done with it, which is what pulls the code out of the text it was copied inside of. The per character filtering of the Type and the Pattern cannot do that on its own for a code of letters, since the letters of the words around it match just as well as the ones of the code: "your code is A1B2C3" would fill the inputs with "YOURCODEI". Returning an empty string rejects the chunk, which raises OnInvalid. It is not applied to a single typed character, and an exception thrown out of it leaves the chunk untouched rather than breaking the input.
Pattern string? null A regular expression that every single character of the code has to match, which is what narrows the code down to a set of characters that no input type covers on its own, like upper case letters or hexadecimal digits. Characters that do not match are rejected while typing and dropped while pasting. An unusable expression is ignored rather than breaking the input.
Placeholder string? null The hint text rendered in the empty inputs. A string as long as the Length is spread over the inputs one character each, any other value is rendered in every input as is.
Reversed bool false Defines whether to render inputs in the opposite direction. The arrow key navigation flips along with it.
Separator string? null The text rendered between the inputs, like a dash or a dot, to make a long code easier to read. It is hidden from assistive technologies and never becomes part of the value.
SeparatorInterval int 1 The number of inputs of each group that the Separator is rendered between, which is how a long code is split into the chunks it is usually printed in, like 123-456. The default is 1, meaning a separator between every pair of inputs. Values below 1 are treated as 1.
SeparatorTemplate RenderFragment<int>? null Custom template rendered between the inputs in place of the Separator text, which is what puts an icon or any other markup between the groups of a code. The context is the zero based index of the input the separator is rendered before, so a template can tell one separator of the row from another. It takes precedence over the Separator.
Sequential bool false Keeps the code free of holes: giving the focus to an input that sits after the first empty one, by clicking it or with an arrow key, moves the focus to that first empty input instead, and a chunk of characters that arrives at once (a paste or an auto fill) cannot land past it either, so the code is always filled from its start onwards. Without it a character typed into the middle of an empty row is reported as if it were the first one of the code, since the value is the characters of the inputs joined together and an empty input contributes nothing to it. A complete code is left alone, so any of its characters can still be clicked and corrected.
SingleTabStop bool false Turns the whole component into a single stop of the tab order: only the input holding the first character of the code is reachable with the Tab key and the rest are left to the auto advancing focus, the arrow keys and the mouse. Tabbing out of the code then lands on the element after it rather than on its next character.
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, which also decides the virtual keyboard of the mobile browsers. The Number type asks for the numeric keypad and rejects every character that is not a digit, whether it is typed or pasted, without rendering a native number input (which would carry spin buttons and report an empty value for characters like e or -). The Email and the Url types are rendered as text inputs for the same reason, since the constraint validation they carry can never be satisfied by a single character and would keep a plain html form from submitting; only the keyboard they ask for is kept.
Uppercase bool false Turns every character of the code into its upper case form as it is typed or pasted, which is what lets a code that is printed in upper case be typed in either case. The conversion happens before the Pattern is applied, so an expression restricted to upper case letters accepts a lower case keystroke instead of rejecting it.
Variant BitVariant? null The visual variant of the inputs, which decides how much of the frame around each input is painted: a full fill, only an outline, or just an underline.
Vertical bool false Defines whether to render inputs vertically. The arrow key navigation follows the layout.

BitOtpInput public members

Name Type Default value Description
InputElements ElementReference[] The ElementReferences to the input elements of the BitOtpInput. The inherited InputElement, which every input component carries a single one of, stands for the input holding the first character of the code.
BlurAsync () => ValueTask Removes the focus from the input of the BitOtpInput that currently holds it, which is what dismisses the virtual keyboard of a phone. Nothing happens when the focus is somewhere else on the page, so a component that filled itself in the background never takes it away from what the user is doing.
Clear () => Task Clears the value of all of the inputs of the BitOtpInput. It does nothing while the component is disabled or read-only.
FocusAsync (int index = 0) => ValueTask Gives focus to a specific input element of the BitOtpInput. The index is clamped into the range of the rendered inputs, and calling it before the component has rendered does nothing rather than asking the browser for an element that is not there yet.

BitInputBase parameters

Name Type Default value Description
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.

BitInputBase public members

Name Type Default value Description
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.

BitComponentBase parameters

Name Type Default value Description
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.

BitComponentBase public members

Name Type Default value Description
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.

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.

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.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.