Input OTP

Accessible one-time password input that renders individual character slots backed by a single hidden text field, with built-in digit filtering, separator groups, error state, and blinking caret.

InputOTP renders a row of character-slot boxes driven by a single invisible BasicTextField. The component automatically filters input to digits when keyboardType is numeric and trims the value to length. Each slot animates its border colour on focus, error, and blur transitions, and shows a blinking caret when it is the active slot. Separator groups can be inserted at arbitrary positions using separatorIndices.

Basic usage

With separator

Pass a Set<Int> of zero-based slot indices to separatorIndices. A separator is rendered after each listed index. The example below splits a 6-digit code into two groups of three.

The separator is not rendered after the last slot even if its index appears in separatorIndices.

Custom length

Use length to control how many slots are displayed. Input beyond that length is silently trimmed by onValueChange.

Error state

Set isError = true to highlight all slot borders with the theme's destructive colour. Pair it with an error message below the field for full accessibility.

Disabled

Set enabled = false to prevent all interaction. The entire row is rendered at 50 % alpha.

API reference

InputOTP

ParameterTypeDefaultDescription
valueStringCurrent OTP string.
onValueChange(String) -> UnitCalled with the filtered and length-trimmed new value.
modifierModifierModifierApplied to the root BasicTextField.
lengthInt6Number of character slots to render.
enabledBooleantrueWhen false, disables interaction and renders at 50 % alpha.
isErrorBooleanfalseWhen true, slot borders switch to the destructive colour.
keyboardTypeKeyboardTypeKeyboardType.NumberPasswordSoft-keyboard type. Numeric types automatically filter non-digit input.
keyboardActionsKeyboardActionsKeyboardActions.DefaultCallbacks for IME actions (e.g. onDone).
separatorIndicesSet<Int>emptySet()Zero-based slot indices after which to render the separator.
separator@Composable () -> Unit{ InputOTPSeparator() }Composable placed between slot groups.
slotSizeDpInputOTPDefaults.SlotSize (40 dp)Width and height of each square slot.
colorsInputOTPStyleInputOTPDefaults.colors()Theme colours for the component.

InputOTPDefaults

MemberDescription
SlotSize: DpDefault slot size — 40.dp.
SlotGap: DpGap between adjacent slots — 0.dp (slots are flush).
GroupGap: DpGap added on each side of a separator — 8.dp.
colors(): InputOTPStyleReturns the default theme-derived colour set.
colors(overrides)Accepts a lambda InputOTPStyle.() -> InputOTPStyle for partial overrides via copy(...).

InputOTPStyle

FieldTypeDescription
backgroundColorSlot background. Defaults to Color.Unspecified (transparent).
textColorCharacter text colour when enabled.
disableTextColorCharacter text colour when disabled.
activeRingColorSemi-transparent ring drawn around the active slot (ring colour at 50 % alpha).
caretColorBlinking caret colour in the active empty slot.
borderInputOTPBorderStyleBorder colours for each slot state.

InputOTPBorderStyle

FieldTypeDescription
defaultColorBorder colour when the slot is idle.
focusColorBorder colour of the active slot.
errorColorBorder colour when isError = true.
Edit this page on GitHub