Inputs

OTP Field

One accessible input, presented as individual code cells.

Installation

pnpm dlx @mielui/svelte add otp-field

Usage

Bind value to the entered code. One native input handles focus, selection, paste, autofill, and form submission; the visible cells are hidden from assistive technology. Name the field with a Label whose for matches Root's id, with aria-label, or with Field.Control. bind:element gives you that input.

Digits are accepted by default and autocomplete defaults to one-time-code. Set pattern and inputmode together for another alphabet, and use pasteTransformer to clean up pasted text. Set minlength to the same number as length, together with required, when the browser must reject incomplete codes. Always verify the code on the server.

onValueChange runs with the whole value on every typed, pasted, or deleted character. onComplete runs with the code once every cell is filled; it does not verify or submit anything. To submit automatically, call the form's requestSubmit() so validation and submit handlers still run. Lowering length truncates the value, a form reset restores the initial value, and a disabled field is left out of the submission.

<script lang="ts">
    import * as OTPField from '@mielui/svelte/components/otp-field';

    let code = $state('');
</script>

<OTPField.Root bind:value={code} aria-label="Verification code" />

Examples

Form submission and reset

Alphanumeric codes

Value changes

Disabled

Parts

Without children, Root renders one Group of Cells. Pass a children snippet to group or restyle the cells it gives you, with a Separator between groups; render every cell, in order. Cell takes bind:element for its element.

textalign aligns the hidden input's text to the left, center, or right, which matters only when you render your own cells. pushPasswordManagerStrategy decides what happens when a password manager adds its badge: increase-width widens the input so the badge sits beside the cells, and none leaves it alone.

<OTPField.Root bind:value={code} aria-label="Verification code">
    {#snippet children({ cells })}
        <OTPField.Group>
            {#each cells.slice(0, 3) as cell, index (index)}
                <OTPField.Cell {cell} />
            {/each}
        </OTPField.Group>
        <OTPField.Separator />
        <OTPField.Group>
            {#each cells.slice(3) as cell, index (index)}
                <OTPField.Cell {cell} />
            {/each}
        </OTPField.Group>
    {/snippet}
</OTPField.Root>

API reference

OtpField.Root

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • disabled
    boolean

    Whether the input is disabled

  • value Bindable
    string

    The value of the input.

    Default ''

  • onValueChange
    OnChangeFn<string>

    A callback function that is called when the value of the input changes.

  • pasteTransformer
    ((text: string) => string)

    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.

  • textalign
    "left" | "center" | "right"

    Customize the alignment of the text within in the input.

  • pushPasswordManagerStrategy
    "increase-width" | "none"

    How to handle the input when a password manager is detected.

  • length
    number

    Number of characters in the code.

    Default 6

  • element Bindable
    HTMLInputElement | null

    Bindable reference to the DOM element.

    Default null

  • onComplete
    ((value: string) => void)

    Called with the code once every cell is filled.

  • children
    Snippet<[{ cells: PinInputCell[]; isFocused: boolean; }]>

    Content rendered inside.

OtpField.Cell

  • id
    string

    Id of the element.

  • style
    string | StyleProperties | null

    Inline styles for the element.

  • cell Required
    { char: string | null | undefined; isActive: boolean; hasFakeCaret: boolean; }

    This specific cell, which is provided by the cells snippet prop from the PinInput.Root component.

  • element Bindable
    HTMLElement | null

    Bindable reference to the DOM element.

    Default null

Other parts

These parts have no props of their own. They pass HTML attributes and events to their element.

  • OtpField.Group
  • OtpField.Separator