OTP Field
Installation
npx @mielui/svelte add otp-fieldyarn dlx @mielui/svelte add otp-fieldbunx @mielui/svelte add otp-fieldUsage
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
0 of 6 digits entered
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
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
disabledbooleanWhether the input is disabled
valueBindablestringThe value of the input.
Default
''onValueChangeOnChangeFn<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.
lengthnumberNumber of characters in the code.
Default
6elementBindableHTMLInputElement | nullBindable reference to the DOM element.
Default
nullonComplete((value: string) => void)Called with the code once every cell is filled.
childrenSnippet<[{ cells: PinInputCell[]; isFocused: boolean; }]>Content rendered inside.
OtpField.Cell
idstringId of the element.
stylestring | StyleProperties | nullInline styles for the element.
cellRequired{ char: string | null | undefined; isActive: boolean; hasFakeCaret: boolean; }This specific cell, which is provided by the
cellssnippet prop from thePinInput.Rootcomponent.elementBindableHTMLElement | nullBindable 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.GroupOtpField.Separator
pnpm dlx @mielui/svelte add otp-field