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:

  1. Resolves the mask pattern from the preset name or data-pattern attribute
  2. Intercepts input events and reformats the value to match the pattern
  3. Manages cursor position so the caret stays in the correct place after reformatting
  4. Handles IME composition events (compositionstart / compositionend) to avoid interfering with multi-byte input
  5. Sets inputmode automatically ("numeric" for digit-only masks)
  6. Sets maxlength from the pattern length
  7. 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-4567 console.log(input.value); // Raw unformatted value: 5551234567 console.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.

// Dynamically added masked inputs are auto-enhanced via MutationObserver const input = document.createElement('input'); input.type = 'tel'; input.dataset.mask = 'phone'; document.body.appendChild(input); // input is ready to use — no manual init needed

Accessibility

  • inputmode="numeric" is set automatically for digit-only masks, showing a numeric keyboard on mobile devices
  • maxlength is derived from the pattern length, giving screen readers the expected input length
  • IME composition is handled correctly — masking pauses during compositionstart and resumes after compositionend
  • Cursor position is managed so the caret never jumps unexpectedly during typing or deletion
  • A visible <label> is required for the input
  • The placeholder attribute should show the expected format as a hint
  • Without JavaScript, the input accepts freeform text — progressive enhancement