@mshafiqyajid/react-input-mask
Headless input mask hook + styled component. Supports custom mask patterns with digit (_), alpha (a), and alphanumeric (*) slots. Fixed literal characters are skipped automatically. Size + tone variants, label, hint, error, controlled or uncontrolled.
Playground #
import { InputMaskStyled } from "@mshafiqyajid/react-input-mask/styled";
import "@mshafiqyajid/react-input-mask/styles.css";
<InputMaskStyled
onChange={(val) => setValue(val)}
mask="phone"
label
/>Install #
npm install @mshafiqyajid/react-input-mask Quick start #
import { InputMaskStyled } from "@mshafiqyajid/react-input-mask/styled";
import "@mshafiqyajid/react-input-mask/styles.css";
const [phone, setPhone] = useState("");
<InputMaskStyled
mask="(___) ___-____"
label="Phone number"
value={phone}
onChange={(value, rawValue) => setPhone(value)}
/> Mask format #
Define your pattern using these special characters:
| Character | Accepts |
|---|---|
_ | Digit (0–9) |
a | Letter (a–z, A–Z) |
* | Alphanumeric (letter or digit) |
| Any other character | Fixed literal — cannot be deleted, cursor skips over it |
// Phone
<InputMaskStyled mask="(___) ___-____" />
// Date
<InputMaskStyled mask="__/__/____" />
// Time
<InputMaskStyled mask="__:__" />
// Credit card
<InputMaskStyled mask="____ ____ ____ ____" />
// Plate (2 letters + 4 digits)
<InputMaskStyled mask="aa-____" /> Custom mask character #
// Use • instead of _ for empty slots
<InputMaskStyled mask="__/__/____" maskChar="•" /> Lazy mode #
By default (lazy is true), mask placeholder characters are only shown up to the next slot to fill. The field looks clean until the user starts typing. Set lazy={false} to always show the full mask — useful for credit card fields that should show the entire pattern immediately.
// Always show the full mask pattern (eager mode)
<InputMaskStyled mask="____ ____ ____ ____" lazy={false} label="Card number" />
// Default: only show mask as far as the user has typed (lazy mode)
<InputMaskStyled mask="____ ____ ____ ____" label="Card number" /> showMask #
Set showMask={false} to hide the maskChar fill characters entirely. Only the typed characters are displayed — useful when you want mask validation without visual placeholders.
<InputMaskStyled mask="(___) ___-____" showMask={false} label="Phone" /> Custom format chars #
Override or extend the default format character map with formatChars. Keys are the characters used in the mask string; values are RegExp patterns that a typed character must match.
// Hex input: H accepts [0-9A-Fa-f]
<InputMaskStyled
mask="HH:HH:HH"
formatChars={{ H: /[0-9A-Fa-f]/ }}
label="MAC segment"
/>
// Pin with uppercase letters only
<InputMaskStyled
mask="UUUU"
formatChars={{ U: /[A-Z]/ }}
label="PIN"
/> Auto-unmask #
When autoUnmask is true, the first argument passed to onChange is the raw value (no mask chars) instead of the formatted display value. The second argument remains rawValue as usual.
// onChange receives "5551234567" instead of "(555) 123-4567"
<InputMaskStyled
mask="(___) ___-____"
autoUnmask
onChange={(val, raw) => console.log(val)} // val === raw === "5551234567"
/> Prefix and suffix #
Use prefix and suffix to render non-editable decorative text inside the input wrapper — before or after the input field.
<InputMaskStyled mask="___-____" prefix="$" label="Amount" />
<InputMaskStyled mask="__/__/____" suffix="cal" label="Date" /> Validation #
Pass an error string to flip tone to danger and show the message below the field. Use hint for helper text.
<InputMaskStyled
mask="__/__/____"
label="Date of birth"
value={value}
onChange={(val, raw) => setValue(val)}
error={raw.length > 0 && raw.length < 8 ? "Enter a complete date" : undefined}
hint="Format: MM/DD/YYYY"
/> Headless #
import { useInputMask } from "@mshafiqyajid/react-input-mask";
const { inputProps, value, rawValue, isComplete, isFocused } = useInputMask({
mask: "(___) ___-____",
onChange: (value, rawValue) => console.log(value, rawValue),
onComplete: (value, rawValue) => submitForm(rawValue),
});
return <input {...inputProps} />; API — InputMaskStyled #
| Prop | Type | Default | Description |
|---|---|---|---|
| mask | string | — | Mask pattern string (required) |
| maskChar | string | "_" | Fill character shown in empty slots |
| value | string | — | Controlled formatted value |
| defaultValue | string | "" | Uncontrolled initial value |
| onChange | (value: string, rawValue: string) => void | — | Called on every change |
| onAccept | (value: string, rawValue: string) => void | — | Called when a character is accepted |
| onComplete | (value: string, rawValue: string) => void | — | Called when all slots are filled |
| onFocus | (e: React.FocusEvent) => void | — | Focus event callback |
| onBlur | (e: React.FocusEvent) => void | — | Blur event callback |
| allowedChars | RegExp | — | Override default per-slot character acceptance |
| formatChars | Record<string, RegExp> | — | Custom format character map; merged with defaults (_=digit, a=alpha, *=any) |
| lazy | boolean | true | When true, mask chars are only shown up to the next empty slot; when false, full mask is always visible |
| showMask | boolean | true | When false, hides the maskChar fill characters — only typed characters are shown |
| autoUnmask | boolean | false | When true, the first onChange argument is the raw value without mask chars |
| prefix | string | — | Non-editable text rendered before the input inside the wrapper |
| suffix | string | — | Non-editable text rendered after the input inside the wrapper |
| size | "sm" | "md" | "lg" | "md" | Size variant |
| tone | "neutral" | "primary" | "success" | "danger" | "neutral" | Color tone |
| disabled | boolean | false | Disable the input |
| readOnly | boolean | false | Read-only mode |
| required | boolean | false | Mark required, adds asterisk to label |
| invalid | boolean | false | Apply invalid/error styling |
| label | string | — | Label rendered above the field |
| hint | string | — | Helper text below the field |
| error | string | — | Error message — auto-applies danger tone + aria-invalid |
API — useInputMask #
| Return | Type | Description |
|---|---|---|
| inputProps | object | Spread onto your <input> |
| value | string | Formatted display value (with mask chars) |
| rawValue | string | User-entered characters only, no mask chars |
| isComplete | boolean | True when all slots are filled |
| isFocused | boolean | True when the input is focused |