PIN Input

One-time-code / PIN entry.

Usage

maxlength is required — it is how many cells there are. Root hands you a cells array through a snippet, so you render them. Use onComplete to submit rather than making the user hunt for a button after typing the last digit.

Verification code

value: (empty)

One input, not six

The cells look like six boxes. They are not six inputs — Bits renders one real input behind them and the cells are purely visual. That is the whole reason to use this instead of rolling your own, and it buys four things a six-input version loses:

  • Paste works. Pasting a six-digit code fills every cell at once.
  • Mobile SMS autofill works — the input carries autocomplete="one-time-code", so the OS offers the code from the message.
  • No tabbing between boxes, and no focus-juggling bugs to write.
  • A screen reader announces one field, not six unlabelled ones.

Use pasteTransformer to clean what arrives — stripping hyphens and spaces means a code copied as 123-456 still lands correctly.

Naming it

<label for> does not work here.

The id you pass lands on the wrapper <div>, and the real input gets a Bits-internal id you cannot predict — so a for attribute has nothing to point at, and you ship an unnamed field. Verified against the rendered DOM, and pinned by a test.

Name it from Root instead; the spread reaches the input:

  • aria-label="Verification code" — simplest.
  • aria-labelledby="my-label-id" — when you want a visible label. Give your own element an id and point at it, as the example above does.

Props

PinInput.Rootvalue is bindable.

PropTypeDefault
value The entered code. Bindable.string''
invalid Marks the control as failing validation. Always applies the invalid styling. Does NOT set `aria-invalid`: this renders as `container; Bits owns the inputs`, and ARIA does not support the attribute there, so assistive technology is free to ignore it. axe does NOT flag it either way — verified by injecting it and watching the suite still pass — so this is a decision taken from the spec, not one a tool enforces. The accessible signal comes from `Field` wiring the error message through `aria-describedby`. Prefer letting `Field` drive this: passing `Field` an `error` is what makes a field invalid, so the message the user reads and the state of the control cannot disagree.booleanfalse
class string
children bits-ui Snippet<[PinInputRootSnippetProps]> | undefined
disabled bits-ui Whether the input is disabledboolean | undefined
id bits-ui string | undefined
inputId bits-ui Optionally provide an ID to apply to the hidden input element.string | undefined
inputRef bits-ui The underlying hidden `<input>` element. Bind to call `focus()`, read selection, etc.HTMLInputElement | null | undefined
maxlength required bits-ui The max length of the input.number
onComplete bits-ui A callback function that is called when the input is completely filled.((...args: any[]) => void) | undefined
onValueChange bits-ui A callback function that is called when the value of the input changes.OnChangeFn<string> | undefined
pasteTransformer bits-ui A callback function that is called when the user pastes text into the input. It receives the pasted text as an argument, and should return the sanitized text. Use this function to clean up the pasted text, like removing hyphens or other characters that should not make it into the input.((text: string) => string) | undefined
pushPasswordManagerStrategy bits-ui How to handle the input when a password manager is detected."increase-width" | "none" | undefined
ref bits-ui HTMLElement | null | undefined
textalign bits-ui Customize the alignment of the text within in the input."center" | "right" | "left" | undefined"left"

PinInput.Cell takes the cell object from the snippet plus class.

PropTypeDefault
class Extra classes merged onto the cell.string
cell required bits-ui This specific cell, which is provided by the `cells` snippet prop from the `PinInput.Root` component.PinInputCell
child bits-ui Snippet<[{ props: Record<string, unknown>; }]> | undefined
children bits-ui Snippet<[]> | undefined
id bits-ui string | undefined
ref bits-ui HTMLElement | null | undefined