Guide
React, Vue, Svelte and a plain page
React
import { MaskedInput } from '@devix-labs/masked-input/react';
<MaskedInput
mask="cnic"
name="cnic"
value={cnic}
onChange={(raw) => setCnic(raw)}
placeholder="42101-1234567-1"
/>
value and onChange are the raw value, which is what you are storing.
The widget owns the field's text and React never writes value to the DOM, so a
re-render in the middle of fast typing cannot move the caret — which is what
react-input-mask's StrictMode issue (#239, +58) is ultimately about. Changing
mask regroups what is typed rather than rebuilding the field.
react-hook-form
<Controller
name="cnic"
control={control}
rules={{ validate: (v) => v?.length === 13 || 'Thirteen digits' }}
render={({ field }) => (
<MaskedInput mask="cnic" value={field.value} onChange={field.onChange} onBlur={field.onBlur} />
)}
/>
Vue
<script setup>
import { ref } from 'vue';
import { MaskedInput } from '@devix-labs/masked-input/vue';
const cnic = ref('');
</script>
<template>
<MaskedInput v-model="cnic" mask="cnic" name="cnic" />
</template>
v-model is the raw value.
Svelte
<script>
import { maskedInput } from '@devix-labs/masked-input/svelte';
let cnic = '';
</script>
<input use:maskedInput={{ mask: 'cnic', onChange: (s) => (cnic = s.raw) }} name="cnic" />
A web component
<script type="module" src="https://devix.pk/cdn/oss/masked-input@1.0.0/element.js"></script>
<dx-masked-input mask="emirates-id" name="eid" placeholder="784-1990-1234567-1"></dx-masked-input>
It creates the <input> itself, or wraps one you put inside — which is the way
to keep a field's own label, attributes and place in the tab order:
<label for="iban">IBAN</label>
<dx-masked-input mask="iban">
<input id="iban" name="iban" inputmode="text">
</dx-masked-input>
It fires change and complete, and exposes .value, .raw and .instance.
Laravel and Blade
The hidden-field behaviour means the server side is ordinary. Nothing in your controller knows a mask was involved:
<form method="post">
@csrf
<input name="cnic" id="cnic" value="{{ old('cnic') }}">
</form>
<script type="module">
import { createMaskedInput } from '/cdn/oss/masked-input@1.0.0/index.js';
createMaskedInput(document.querySelector('#cnic'), { mask: 'cnic' });
</script>
$request->validate(['cnic' => ['required', 'digits:13', new Cnic]]);
old('cnic') is the raw value, and the widget re-masks it on load, so a failed
validation redisplays correctly — which is the thing that usually breaks.
Devix Laravel Validators ships the Cnic, EmiratesId, Iban and the rest of
those rules, checking the same lengths this package masks.
Enhancing a field you already have
None of the above is required. The widget takes an existing <input> and leaves
everything about it alone except the value:
document.querySelectorAll('[data-mask]').forEach((field) => {
createMaskedInput(field, { mask: field.dataset.mask });
});
<input name="cnic" data-mask="cnic">
<input name="card" data-mask="card">
<input name="mac" data-mask="mac">