TypeScript input masking
TypeScript input masks for browser forms.
Bind a pattern to any input and keep edits, selections, and pasted values formatted without a framework dependency.
npm install mother-mask
bind(input, '(999) 999-9999')
Lightweight JavaScript input masking
mother-mask formats form fields as people type,
paste, select, and replace text. Use it in plain
browser apps or alongside any UI framework
because the package only needs an
HTMLInputElement.
Pattern masks
Add phone, date, CPF, CNPJ, CEP, SSN,
ZIP+4, postal code, IBAN, VAT ID,
license plate, credit card, and custom
input masks with bind().
Decimal and currency masks
Format money, quantities, localized
separators, prefixes, suffixes, and
negative values with
bindDecimal() and decimal
helper exports.
Small browser package
Ships with zero runtime dependencies, TypeScript types, ESM, CJS, and UMD builds for npm installs or direct CDN usage.
Examples
Every field below is live.
CPF
bind(input, '999.999.999-99')
11 digits
CNPJ (alphanumeric)
bind(input, 'AA.AAA.AAA/AAAA-99')
12 alphanumeric + 2 digits
CEP
bind(input, '99999-999')
8 digits
Phone — array mask
bind(input, ['(99) 9999-9999', '(99)
99999-9999'])
10 or 11 digits — mask switches
automatically
Date — segmented (default)
bind(input, '99/99/9999')
select "12" and retype — day and year
stay put
Date — flat (segmented: false)
bind(input, '99/99/9999', { segmented:
false })
same edit here pulls the year
forward
Time
bind(input, '99:99')
simple HH:MM bind mask
License plate
bind(input, 'ZZZ-9999', { segmented:
false })
letters then digits
Mercosul plate
bind(input, 'ZZZ-9Z99')
letter/digit mixed pattern
Credit card — array mask
bind(input, ['9999 999999 99999', '9999
9999 9999 9999'])
Amex (15) vs Visa/Mastercard (16)
Currency (USD-style)
bindDecimal(input, { prefix: '$'
})
Currency (EUR-style)
bindDecimal(input, { separator: '.',
decimalSeparator: ',', suffix: ' €'
})
"." also opens the fraction
Whole numbers
bindDecimal(input, { decimalPlaces: 0,
suffix: ' units' })
Negative values
bindDecimal(input, { prefix: '$',
allowNegative: true })
type "-" anywhere to flip the
sign
Masked value vs. raw value
bind(input, '999.999.999-99', (value)
=> { masked, raw })
Regional formats
A few common US, Canada, and Europe patterns —
all just bind() calls.
US — Phone
bind(input, '(999) 999-9999')
10 digits
US — SSN
bind(input, '999-99-9999')
9 digits
US — ZIP+4
bind(input, '99999-9999')
5, or 9 with the +4 suffix
Canada — postal code
bind(input, 'Z9Z 9Z9')
6 alphanumeric characters
Canada — SIN
bind(input, '999 999 999')
9 digits
Europe (DE) — IBAN
bind(input, 'ZZ99 9999 9999 9999 9999
99')
2 letters + 20 digits
Europe (DE) — VAT ID
bind(input, 'DE999999999')
9 digits after the DE prefix
Europe (PL) — postal code
bind(input, '99-999')
5 digits
Pattern syntax
| Character | Matches |
|---|---|
9 |
Digit
(0–9)
|
Z |
Letter
(a–z,
A–Z)
|
A |
Alphanumeric (digit or letter) |
| Anything else | Literal — inserted as the user fills slots |
Array masks: pass patterns shortest → longest; the active one is picked from how many data characters are typed (see phone and credit card above).
UMD / CDN
<script src="https://unpkg.com/mother-mask/dist/mother-mask.umd.js"></script>
<script>
const dispose = MotherMask.bind(document.getElementById('cpf'), '999.999.999-99')
</script>
The global name is MotherMask.
API reference
bind (primary)
| Signature |
bind(input, mask,
options?)
|
| Returns |
() => void —
removes listeners and
attributes so the input can
be bound again
|
| Third argument |
{ onChange?: (value:
string) => void
}, or a legacy
(value) =>
void
callback
|
Other exports
| Export | Description |
|---|---|
buildMask(value, mask,
caret?, options?)
|
Build a
Mask instance
(array mask resolved to one
string first).
|
getMaxLength(mask)
|
Maximum string length for the mask (longest pattern for array masks). |
applyMask(value, mask,
inputCaret?,
options?)
|
Low-level: apply a single
mask string; returns
{ value, caret }.
|
process(value, mask,
options?)
|
Apply a mask pattern to a raw value, returning just the masked string. |
bindDecimal
| Signature |
bindDecimal(input,
options?)
|
| Returns |
() => void —
removes listeners and
attributes so the input can
be bound again
|
| Second argument |
DecimalMaskOptions
& { onChange?:
(value: string,
numericValue: number)
=> void }, or a legacy
(value, numericValue)
=> void
callback
|
Other decimal exports
| Export | Description |
|---|---|
applyDecimalMask(value,
inputCaret?,
options?)
|
Low-level: format a
raw/already-masked value;
returns
{ value, caret }.
|
processDecimal(value,
options?)
|
Apply a decimal mask to a raw value, returning just the masked string. |
unmaskDecimal(value,
options?)
|
Parse a raw or masked
decimal string back into a
JS
number (0
if it has no digits).
|
formatDecimalValue(value,
options?)
|
Format a plain JS
number into its
masked display string —
useful to pre-populate an
input.
|
Types
type MaskPattern = string | string[]
interface MaskResult {
readonly value: string
readonly caret: number
}
interface ApplyMaskOptions {
segmented?: boolean // hard field boundaries — on by default
}
interface BindOptions extends ApplyMaskOptions {
onChange?: (value: string) => void
}
interface DecimalMaskOptions {
decimalPlaces?: number // default 2
segmented?: boolean // group into thousands — default true
separator?: string // thousands separator — default ','
decimalSeparator?: string // default '.'
prefix?: string // default ''
suffix?: string // default ''
allowNegative?: boolean // default false
}
interface BindDecimalOptions extends DecimalMaskOptions {
onChange?: (value: string, numericValue: number) => void
}