SvOtpInput

Segmented one-time-code / PIN entry - N single-character cells that advance themselves and accept a pasted code.

SvOtpInput is the control for verification codes and PINs. It renders length single-character cells with auto-advance, Backspace-to-previous, arrow navigation, and paste that distributes a copied code across the cells. It emits the joined string and fires onComplete the moment every cell is filled. The label / hint / error chrome comes from SvField, so it matches the rest of the kit.

Basic usage

<script lang="ts">
  import { SvOtpInput } from '@svgrid/grid'
  let code = $state('')
</script>

<SvOtpInput
  label="Verification code"
  length={6}
  bind:value={code}
  onComplete={(v) => verify(v)}
/>

Props

SvOtpInput uses a subset of the shared SvEditorProps (disabled, label, hint, error, required, dir, size, id, name, invalid) plus:

Prop Type Default Description
value string '' The joined code. Bindable with bind:value.
onChange (value: string) => void - Fires whenever a cell changes.
onComplete (value: string) => void - Fires once every cell is filled.
length number 6 Number of cells.
numeric boolean true Digits only vs any character.
mask boolean false Render as password dots.
autofocus boolean false Focus the first cell on mount.

Patterns

Verify on completion

onComplete fires exactly once when the last cell fills, so you can submit without a separate button:

<SvOtpInput length={6} bind:value={code} onComplete={submitCode} autofocus />

Alphanumeric or masked codes

Turn off numeric for letters-and-digits codes, or turn on mask for a sensitive PIN:

<SvOtpInput length={4} numeric={false} mask bind:value={pin} />

Paste a whole code

Copying 123456 and pasting into any cell distributes the characters across the remaining cells automatically - no per-cell typing needed.

Verify, then show a retry on failure

onComplete gives you the perfect hook to check the code and, on failure, flag the cells with invalid / error and clear the value so the user can re-enter:

<script lang="ts">
  import { SvOtpInput } from '@svgrid/grid'
  let code = $state('')
  let status = $state<'idle' | 'checking' | 'bad'>('idle')
  async function check(v: string) {
    status = 'checking'
    const ok = await verify(v)
    if (!ok) { status = 'bad'; code = '' }
  }
</script>

<SvOtpInput
  label="Enter the 6-digit code"
  length={6}
  bind:value={code}
  onComplete={check}
  invalid={status === 'bad'}
  error={status === 'bad' ? 'That code is incorrect, try again' : undefined}
  autofocus
/>

Tip: keep numeric on (the default) for SMS codes - it sets inputmode="numeric" so phones show a digit keypad, and the first cell carries autocomplete="one-time-code" for auto-fill.

Accessibility

See also