SutraUI.InputOTP (Sutra UI v0.4.0)
View SourceOne-time password and PIN input with individual digit slots.
Handles paste distribution, arrow-key navigation, and backspace movement.
Submits via a hidden aggregate input so it works with Phoenix forms. Emits
an optional on_complete event when all slots are filled.
Examples
# Simple — 6 digits
<.input_otp id="verify" name="code" length={6} />
# Masked PIN
<.input_otp id="pin" name="pin" length={4} mask placeholder="•" />
# Grouped with separator
<.input_otp id="verify" name="code" length={6} groups={[3, 3]}>
<:separator>—</:separator>
</.input_otp>
# Auto-verify when complete
<.input_otp id="verify" name="code" length={6} on_complete="verify_code" />Form Integration
The component renders a hidden <input name="..."> that aggregates the slot
values. Use it like any form field:
<.form for={@form} phx-submit="save">
<.input_otp id="otp" name={@form[:code].name} value={@form[:code].value}
length={6} invalid={@form[:code].errors != []} />
</.form>Attributes
id- Required. Unique identifier.name- Required. Hidden input name submitted with the form.value- Current OTP value. Defaults to"".length- Number of slots. Defaults to6.groups- Optional list of group sizes for auto-generated grouped slots.pattern- Single-character validation regex. Defaults to"[0-9]".placeholder- Slot placeholder.mask- Render password-style slots. Defaults tofalse.disabled- Disable all slots. Defaults tofalse.invalid- Mark slots invalid (addsaria-invalid). Defaults tofalse.on_complete- LiveView event emitted when all slots are filled.class- Additional CSS classes.
Slots
:group- Optional custom grouping. Useinput_otp_slot/1inside for full control.:separator- Visual separator rendered between groups.
Accessibility
- Each slot has an
aria-label("Digit 1 of 6"). aria-invalidis set only wheninvalid={true}— not unconditionally.- First slot sets
autocomplete="one-time-code"for SMS autofill. - Arrow keys navigate between slots; Backspace moves to the previous slot.
Summary
Functions
Renders a single OTP slot. Use inside a :group slot on input_otp.
Functions
Attributes
id(:string) (required) - Unique identifier for the OTP input.name(:string) (required) - Hidden input name submitted with the form.value(:string) - Current OTP value. Defaults to"".length(:integer) - Number of OTP slots. Defaults to6.groups(:list) - Optional group sizes for generated slots. Defaults tonil.pattern(:string) - Single-character validation regex. Defaults to"[0-9]".placeholder(:string) - Slot placeholder. Defaults tonil.mask(:boolean) - Use password fields. Defaults tofalse.disabled(:boolean) - Disable all slots. Defaults tofalse.invalid(:boolean) - Mark slots invalid. Defaults tofalse.on_complete(:string) - LiveView event emitted when all slots are filled. Defaults tonil.class(:any) - Additional CSS classes. Defaults tonil.- Global attributes are accepted. Additional HTML attributes. Supports all globals plus:
["autocomplete", "aria-label"].
Slots
group- Custom slot groups.separator- Visual separator between groups.
Renders a single OTP slot. Use inside a :group slot on input_otp.
Examples
<.input_otp_slot index={0} />
<.input_otp_slot index={1} type="password" placeholder="•" />Attributes
index(:integer) (required) - Zero-based slot index.type(:string) - Input type — inherited from parent. Defaults to"text".value(:string) - Current slot value — inherited from parent. Defaults tonil.placeholder(:string) - Slot placeholder — inherited from parent. Defaults tonil.disabled(:boolean) - Disabled state — inherited from parent. Defaults tofalse.invalid(:boolean) - Invalid state — inherited from parent. Defaults tofalse.length(:integer) - Total slot count — for aria-label. Defaults to6.class(:any) - Additional CSS classes. Defaults tonil.- Global attributes are accepted. Additional HTML attributes.