data-mask
Format input values as the user types with preset masks for phone, credit card, date, and more. Supports custom patterns with digit, letter, and wildcard tokens.
Overview
The data-mask attribute formats input values in real time as the user types. Choose from preset masks for common formats or define custom patterns with digit, letter, and wildcard tokens.
<label for="phone">Phone number</label><input type="tel" id="phone" data-mask="phone" placeholder="(555) 123-4567">
How It Works
Add data-mask with a preset name or "custom" to any text input. The init script:
- Resolves the mask pattern from the preset name or
data-patternattribute - Intercepts
inputevents and reformats the value to match the pattern - Manages cursor position so the caret stays in the correct place after reformatting
- Handles IME composition events (
compositionstart/compositionend) to avoid interfering with multi-byte input - Sets
inputmodeautomatically ("numeric"for digit-only masks) - Sets
maxlengthfrom the pattern length - Stores the raw unformatted value in
data-raw-value
The underlying input remains a real form control. The displayed value includes formatting characters, while data-raw-value provides the clean value for submission or processing.
Attributes
| Attribute | Type | Description |
|---|---|---|
data-mask |
string | Preset name (phone, credit-card, date, ssn, zip) or custom. |
data-pattern |
string | Custom mask pattern. Required when data-mask="custom". Uses token characters for placeholders. |
data-raw-value |
string | Set automatically. Contains the unformatted input value (digits/letters only, no separators). |
data-mask-init |
boolean | Set automatically to prevent double-binding. Do not set manually. |
Preset Masks
The following presets are available out of the box:
| Value | Pattern | Example output |
|---|---|---|
phone |
(###) ###-#### |
(555) 123-4567 |
credit-card |
#### #### #### #### |
1234 5678 9012 3456 |
date |
##/##/#### |
01/15/2025 |
ssn |
###-##-#### |
123-45-6789 |
zip |
##### |
90210 |
<label for="phone-demo">Phone</label><input type="tel" id="phone-demo" data-mask="phone" placeholder="(555) 123-4567"> <label for="card-demo">Credit card</label><input type="text" id="card-demo" data-mask="credit-card" placeholder="1234 5678 9012 3456"> <label for="date-demo">Date</label><input type="text" id="date-demo" data-mask="date" placeholder="MM/DD/YYYY"> <label for="ssn-demo">SSN</label><input type="text" id="ssn-demo" data-mask="ssn" placeholder="123-45-6789"> <label for="zip-demo">ZIP</label><input type="text" id="zip-demo" data-mask="zip" placeholder="12345">
Custom Patterns
Set data-mask="custom" and provide a data-pattern attribute with your own mask. Three token characters are supported:
| Token | Accepts | Description |
|---|---|---|
# |
Digit (0–9) | Matches any single digit. |
A |
Letter (a–z, A–Z) | Matches any single letter. Output is uppercased. |
* |
Any character | Matches any single letter or digit. Output is uppercased. |
Any other character in the pattern is treated as a literal separator and inserted automatically.
<!-- License plate: ABC-1234 --><label for="plate">License plate</label><input type="text" id="plate" data-mask="custom" data-pattern="AAA-####" placeholder="ABC-1234"> <!-- Product key: A1B2-C3D4-E5F6 --><label for="product-key">Product key</label><input type="text" id="product-key" data-mask="custom" data-pattern="****-****-****" placeholder="A1B2-C3D4-E5F6">
With Form Field
Wrap in <form-field> for validation feedback, helper text, and required indicators.
<form-field> <label for="phone-field">Phone number</label> <input type="tel" id="phone-field" data-mask="phone" required placeholder="(555) 123-4567"> <small slot="help">US phone numbers only.</small></form-field>
Raw Value
The data-raw-value attribute is updated on every input event and contains the unformatted value — digits and letters only, with no separators. Use it when submitting or processing the value.
const input = document.querySelector('[data-mask="phone"]'); // Display value: (555) 123-4567console.log(input.value); // Raw unformatted value: 5551234567console.log(input.dataset.rawValue);
Events
The masked input fires native input and change events. Access the raw value from data-raw-value in your event handler.
const input = document.querySelector('[data-mask]'); input.addEventListener('input', () => { console.log('Formatted:', input.value); console.log('Raw:', input.dataset.rawValue);});
Dynamic Elements
Masked inputs added to the DOM after page load are automatically enhanced via a MutationObserver. No manual initialization is needed.
<section> <h2>Accessibility</h2> <ul> <li><code>inputmode="numeric"</code> is set automatically for digit-only masks, showing a numeric keyboard on mobile devices</li> <li><code>maxlength</code> is derived from the pattern length, giving screen readers the expected input length</li> <li>IME composition is handled correctly — masking pauses during <code>compositionstart</code> and resumes after <code>compositionend</code></li> <li>Cursor position is managed so the caret never jumps unexpectedly during typing or deletion</li> <li>A visible <code><label></code> is required for the input</li> <li>The <code>placeholder</code> attribute should show the expected format as a hint</li> <li>Without JavaScript, the input accepts freeform text — progressive enhancement</li> </ul> </section>