Input Mask
Native text input driven by a typed pattern mask (phone numbers, dates, card numbers, codes).
| Package | @vizejs/ui/input-mask |
| Maturity | stable |
| Own the source | vize lib pull input-mask |
| Requires | controllable-state, id |
| Aliases | masked input, input mask, phone input, pattern input, formatted input |
| Covers | HTML pattern attribute, imask, maska, vue-the-mask |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { MaskedInput } from "@vizejs/ui/input-mask";
Or copy the source into your project with vize lib pull input-mask (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Phone number field that masks digits as the user types and reports completion. -->
<script setup lang="ts">
import { ref, useId } from "vue";
import { MaskedInput, type InputMaskResult } from "@vizejs/ui/input-mask";
const phone = ref("");
const lastComplete = ref("");
const inputId = useId();
const hintId = useId();
function onComplete(result: InputMaskResult): void {
lastComplete.value = result.masked;
}
</script>
<template>
<div>
<label :for="inputId">Phone number</label>
<MaskedInput
:id="inputId"
v-model="phone"
mask="(999) 999-9999"
name="phone"
autocomplete="tel-national"
:aria-describedby="hintId"
@complete="onComplete"
/>
<p :id="hintId">Ten digits, area code first.</p>
<output :for="inputId"
>Digits: {{ phone || "none" }}. Last complete number: {{ lastComplete || "none" }}</output
>
</div>
</template>
API
MaskedInput
Source: src/families/form/input-mask/masked-input.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
mask (required) |
string |
required |
Mask pattern. 9 digit, a letter, * letter or digit, \ escapes a literal. |
tokens |
InputMaskTokens |
undefined |
Extra or overriding tokens (see defineInputMaskTokens). |
placeholderChar |
string |
"_" |
Placeholder character for unfilled slots when lazy is false. |
lazy |
boolean |
true |
Hide unfilled slots; false shows the whole mask with placeholders. |
eager |
boolean |
false |
Append literals right after the last filled slot while typing. |
valueFormat |
InputMaskValueFormat |
"raw" |
Representation used by modelValue, defaultValue, and emits. |
modelValue |
string |
undefined |
Controlled model value. undefined selects uncontrolled behavior. |
defaultValue |
string |
"" |
Initial uncontrolled value, also restored by form reset. |
id |
string | null |
undefined |
Consumer-owned input id. null and undefined select a deterministic fallback. |
name |
string |
undefined |
Native form field name; the displayed (masked) text is submitted. |
disabled |
boolean |
false |
Disable editing, focus, and native form submission. |
readOnly |
boolean |
false |
Keep the input focusable while preventing edits. |
required |
boolean |
false |
Mark the input as required for native constraint validation. |
placeholder |
string |
undefined |
Native placeholder shown while empty. |
autocomplete |
string |
undefined |
Native autocomplete hint. |
inputMode |
"decimal" | "email" | "numeric" | "search" | "tel" | "text" | "url" |
undefined |
Virtual keyboard hint. Defaults to "numeric" for digit-only masks. |
ariaLabel |
string |
undefined |
Accessible name when no label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the input. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the input. |
ariaErrormessage |
string |
undefined |
Id of the validation error message used while invalid. |
ariaInvalid |
MaskedInputAriaInvalid |
false |
Invalid state announced to assistive technology. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: string] |
Fired when the model value (in valueFormat) changes. |
complete |
[result: InputMaskResult] |
Fired when the last slot is filled, with the conform result. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLInputElement | null |
Rendered native input. |
masked |
string |
Displayed text. |
raw |
string |
Characters accepted into slots. |
complete |
boolean |
Whether every slot is filled. |
focus |
(options?: FocusOptions) => void |
Move focus to the input. |
setValue |
(text: string) => boolean |
Conform and store text; returns whether the model changed. |
reset |
() => boolean |
Restore the default value; returns whether the model changed. |
Behavior
Normative state x input -> outcome table for masked-input.vue, the
useInputMask composable, and the pure createInputMask engine
(@vizejs/ui/input-mask). Every row is proven by the named test in
input-mask.test.ts or input-mask-ssr.test.ts; compile-only assertions live
in input-mask.types.test-d.ts.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| M1 | lazy mask | conform typed/pasted/masked text | accepted characters fill token slots in order, literals are inserted only before filled slots, rejects are skipped, complete when all filled |
conforms typed, pasted, and pre-masked text to token slots and literals |
| M2 | eager / placeholder | conform | eager appends following literals; lazy=false shows placeholders; \ escapes tokens; custom tokens transform and keep literal keys |
supports eager literals, placeholders, escapes, and custom typed tokens |
| M3 | composable, no instance | setValue / controlled value |
runs in any effect scope, reports changes and a single completion, controlled values win | useInputMask works outside components and reports completion |
| M4 | seeded | render | native text input with deterministic id, masked value, numeric inputmode for digit masks, form and ARIA attributes, data-state |
renders a native text input with mask, form, and accessibility hooks |
| M5 | focused | type / paste | value conforms, caret lands after the last affected slot, update:modelValue emits raw text, complete fires once |
typing conforms the value, keeps the caret after the slot, and emits once complete |
| M6 | caret next to a literal | Backspace / Delete | deleting only a literal removes the neighboring slot character instead of being undone | Backspace and Delete across a literal remove the neighboring slot character |
| M7 | valueFormat="masked" |
type / controlled | model value includes literals and placeholders; controlled values win until accepted | masked value format, placeholders, and controlled values |
| M8 | in a form | reset / disabled / read-only | form reset restores the default display; disabled and read-only publish their data-state |
form reset restores the default and disabled or read-only states publish |
| M9 | imperative | expose | focus, setValue, reset, masked, raw, complete, element |
exposes focus, setValue, reset, and mask state |
| M10 | inside Field |
v-bind="fieldProps" |
id and ARIA relations reach the native input | binds Field fieldProps for label, description, and error wiring |
| M11 | SSR / hydration | isolated requests | byte-identical markup (including placeholders) and hydration without diagnostics; hydrated input stays interactive | renders byte-identical masked markup and hydrates without mismatches |
Public extension contract
| Surface | Contract |
|---|---|
| Parts | input on the native <input type="text">. |
| Data attributes | data-vize-ui="masked-input", data-state, data-complete ("true"/"false"). |
| Tokens | 9 digit, a ASCII letter, * ASCII letter or digit; extend with defineInputMaskTokens. |
| Composable | useInputMask binds any input via :value="masked" and @input="handleInput". |
The subpath is tree-shakable and ships no CSS; those package contracts are
pinned by distribution.test.ts, check:size, and check:tree-shaking.