SvMaskedInput

A pattern-masked text input that formats as you type and reports both the masked and the raw value.

SvMaskedInput enforces a fixed shape - dates, card numbers, license keys, and the like. In the mask string # accepts a digit, A a letter, and * any alphanumeric; every other character is a literal drawn in place. onChange hands you the masked display value, the raw (unmasked) value, and a complete flag, so you can validate on completeness. It is the styled renderer over the headless createMaskedInput core. Its label / hint / error chrome comes from SvField.

Related: SvPhoneInput · SvTextInput · Inputs overview

Installation

Add it with the CLI - this drops a ready-to-edit SvMaskedInput starter into your app:

Prefer to see it first? npx @svgrid/ui try masked-input opens it in a throwaway sandbox - no project needed.

Or install the package and import it directly. SvMaskedInput ships free in @svgrid/grid and is part of the grid's editor kit - the same control SvGrid mounts when you edit a matching cell:

The examples on this page import from @svgrid/grid:

<script lang="ts">
  import { SvMaskedInput } from '@svgrid/grid'

  // The bound value behind each example below.
  let phone = $state('')
</script>
import { SvMaskedInput } from '@svgrid/grid'

Example

Open the live example: Masked input (Inputs)

<script lang="ts">
  import { SvMaskedInput } from '@svgrid/grid'
  let card = $state('')
  let complete = $state(false)
</script>

<SvMaskedInput
  label="Card number"
  mask="#### #### #### ####"
  bind:value={card}
  onChange={(masked, raw, done) => (complete = done)}
/>

Props

SvMaskedInput extends the shared SvEditorProps (disabled, readonly, required, invalid, error, label, hint, size, dir, name, id, ariaLabel) and adds:

Prop Type Default Description
value string '' The masked value.
onChange (masked: string, raw: string, complete: boolean) => void - Fires with masked + raw value and done flag.
mask string '' Pattern: # digit, A letter, * alnum.
placeholder string - Empty-state hint text.
clearable boolean false Show a clear (x) button when there is a value.
leading Snippet - Leading adornment (icon/button) inside the field.
trailing Snippet - Trailing adornment (icon/button) inside the field.
prefix string - Plain-text affix at the start.
suffix string - Plain-text affix at the end.
block boolean false Stretch to the container width.
width number 200 Control width in px (ignored when block).
prefixIcon Snippet - Deprecated alias for leading.
suffixIcon Snippet - Deprecated alias for trailing.

The box, size, invalid state, clear button and adornments are owned by SvField's shared frame chrome.

Examples

Validate on completeness

Use the third onChange argument to gate submission until the mask is fully filled, and keep the raw value for storage:

<SvMaskedInput
  label="Expiry"
  mask="##/##"
  onChange={(m, raw, done) => { expiry = raw; ready = done }}
/>

Icon adornments

Pass a leading / trailing snippet to frame the field (prefixIcon / suffixIcon are kept as deprecated aliases):

<SvMaskedInput mask="(###) ###-####" bind:value={phone}>
  {#snippet leading()}<PhoneIcon />{/snippet}
</SvMaskedInput>

Gate a submit on the raw value

Keep the raw (unmasked) value for storage, flag an incomplete entry through invalid / error, and disable the action until every slot is filled. The * token accepts any alphanumeric character:

<script lang="ts">
  import { SvMaskedInput } from '@svgrid/grid'
  let display = $state('')
  let raw = $state('')
  let complete = $state(false)
</script>

<SvMaskedInput
  label="Serial key"
  mask="****-****-****"
  bind:value={display}
  clearable
  invalid={!!display && !complete}
  error={display && !complete ? 'Key is incomplete' : undefined}
  onChange={(masked, unmasked, done) => { raw = unmasked; complete = done }}
/>
<button disabled={!complete}>Activate</button>

Tip: when you set name, the hidden form input carries the unmasked value (not the display value), so a plain form post submits clean digits/letters.

Accessibility

More examples

Masked input - headless

One createMaskedInput drives the styled field and a custom boxed field with a complete/partial badge.

Open the live example: Masked input - headless (Headless Editors)

Sizes

Every control takes the same three sizes, so a dense toolbar and a roomy form can share components.

<script lang="ts">
  import { SvMaskedInput } from '@svgrid/grid'

  let card = $state('')
</script>

<SvMaskedInput bind:value={card} size="sm" />
<SvMaskedInput bind:value={card} size="md" />
<SvMaskedInput bind:value={card} size="lg" />

In a form

The shared field props behave the same on every editor: label names it, hint explains it, and error plus invalid mark it - which is why a validated form does not need per-component handling.

<script lang="ts">
  import { SvMaskedInput } from '@svgrid/grid'

  let card = $state('')
</script>

<SvMaskedInput
  bind:value={card}
  label="Label"
  hint="A short hint"
  required
/>

<SvMaskedInput
  bind:value={card}
  label="Label"
  error="Something is wrong"
  invalid
/>

See also

Live examples

  • Masked input - SvMaskedInput on its own: a fixed-pattern mask (# digit, A letter, * alphanumeric; other chars literal) emitting the masked + raw value and a complete flag.
  • Masked input - headless - One createMaskedInput drives the styled field and a custom boxed field with a complete/partial badge.