Masked input component for React with format characters, cursor management, and advanced configuration
npx @tessl/cli install tessl/npm-react-input-mask@2.0.0React Input Mask is a masked input component for React that enables developers to create input fields with predefined formatting patterns (masks) for common data types like phone numbers, dates, ZIP codes, and other structured text formats.
npm install react-input-maskimport InputMask from "react-input-mask";For CommonJS:
const InputMask = require("react-input-mask");UMD (browser global):
<script src="https://unpkg.com/react-input-mask/dist/react-input-mask.min.js"></script>
<!-- Available as window.ReactInputMask -->import React from "react";
import InputMask from "react-input-mask";
function PhoneInput() {
const [value, setValue] = React.useState("");
return (
<InputMask
mask="+1 (999) 999-9999"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter phone number"
/>
);
}
// Date input with custom mask character
function DateInput() {
return (
<InputMask
mask="99/99/9999"
maskChar=" "
placeholder="MM/DD/YYYY"
/>
);
}React Input Mask consists of a single React component that wraps standard HTML input elements:
Create input fields with predefined format patterns using format characters.
/**
* React Input Mask Component - Main export
* @param props - Component props including mask, value, and standard input props
* @returns React element with masked input functionality
*/
function InputMask(props: InputMaskProps): React.ReactElement;
interface InputMaskProps {
/** Mask pattern string using format characters (9, a, *) */
mask?: string;
/** Character to show in unfilled mask positions (default: '_') */
maskChar?: string | null;
/** Custom format character definitions */
formatChars?: { [key: string]: string };
/** Show mask when input is empty and unfocused */
alwaysShowMask?: boolean;
/** Ref callback for accessing input DOM node */
inputRef?: (ref: HTMLInputElement) => void;
/** Advanced callback for custom masking logic (experimental) */
beforeMaskedValueChange?: BeforeMaskedValueChangeHandler;
/** Render prop for custom input components (experimental) */
children?: (inputProps: any) => React.ReactElement;
// Standard HTML input props
value?: string;
defaultValue?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
onPaste?: (event: React.ClipboardEvent<HTMLInputElement>) => void;
onMouseDown?: (event: React.MouseEvent<HTMLInputElement>) => void;
disabled?: boolean;
readOnly?: boolean;
placeholder?: string;
className?: string;
style?: React.CSSProperties;
[key: string]: any; // All other HTML input attributes
}Usage Examples:
// Phone number mask
<InputMask mask="+1 (999) 999-9999" />
// Date mask
<InputMask mask="99/99/9999" />
// Social Security Number
<InputMask mask="999-99-9999" />
// Credit Card
<InputMask mask="9999 9999 9999 9999" />
// Custom format with letters and numbers
<InputMask mask="aa-999-aa" />Define input patterns using built-in format characters or create custom ones.
/**
* Default format characters for common input patterns
*/
interface DefaultFormatChars {
/** Digits 0-9 */
'9': '[0-9]';
/** Letters A-Z, a-z */
'a': '[A-Za-z]';
/** Alphanumeric A-Z, a-z, 0-9 */
'*': '[A-Za-z0-9]';
}
/**
* Custom format character definitions
* Keys are format characters, values are RegExp strings
*/
interface FormatChars {
[formatChar: string]: string;
}Usage Examples:
// Using default format characters
<InputMask mask="999-aaa-***" />
// Custom format characters
<InputMask
mask="yyy-xxx"
formatChars={{
'y': '[0-9A-F]', // Hexadecimal
'x': '[AEIOU]' // Vowels only
}}
/>Control the appearance of unfilled mask positions.
/**
* Mask character options for unfilled positions
*/
type MaskChar = string | null;Usage Examples:
// Default underscore mask character
<InputMask mask="999-999-9999" />
// Shows: ___-___-____
// Custom mask character
<InputMask mask="999-999-9999" maskChar=" " />
// Shows: - -
// No mask character (empty spaces)
<InputMask mask="999-999-9999" maskChar={null} />
// Shows:
// Always show mask even when empty
<InputMask mask="999-999-9999" alwaysShowMask />Include literal characters in masks that would otherwise be interpreted as format characters.
Usage Examples:
// German phone with literal +49 prefix
<InputMask mask="+4\\9 99 999 99" />
// Email-like pattern with literal @ and .
<InputMask mask="aaa\\@aaa\\.aaa" />Implement complex masking behavior with the beforeMaskedValueChange callback.
/**
* Advanced masking callback for custom logic (experimental)
* @param newState - New input state with value and selection
* @param oldState - Previous input state
* @param userInput - Raw user input string (null for non-user changes)
* @param maskOptions - Current mask configuration
* @returns Modified state with value and selection
*/
type BeforeMaskedValueChangeHandler = (
newState: MaskedValueChangeState,
oldState: MaskedValueChangeState,
userInput: string | null,
maskOptions: MaskOptions
) => MaskedValueChangeState;
interface MaskedValueChangeState {
/** Current input value */
value: string;
/** Current selection (null on blur or before mount) */
selection: { start: number; end: number } | null;
}
interface MaskOptions {
/** Parsed mask string */
mask: string;
/** Current mask character */
maskChar: string;
/** Whether to always show mask */
alwaysShowMask: boolean;
/** Format character definitions */
formatChars: { [key: string]: string };
/** Array of permanent character positions */
permanents: number[];
}Usage Examples:
// ZIP code with conditional dash
function ZipCodeInput() {
const beforeMaskedValueChange = (newState, oldState, userInput) => {
let { value } = newState;
let { selection } = newState;
// Remove trailing dash if not entered by user
if (value.endsWith('-') && userInput !== '-' && !oldState.value.endsWith('-')) {
if (selection && selection.start === value.length) {
selection = { start: selection.start - 1, end: selection.start - 1 };
}
value = value.slice(0, -1);
}
return { value, selection };
};
return (
<InputMask
mask="99999-9999"
maskChar={null}
beforeMaskedValueChange={beforeMaskedValueChange}
/>
);
}Use custom input components with render props pattern.
/**
* Render prop function for custom input components (experimental)
* @param inputProps - Props to spread onto custom input component
* @returns Custom input element
*/
type ChildrenRenderProp = (inputProps: InputProps) => React.ReactElement;
interface InputProps {
// All props except controlled ones (onChange, onPaste, etc.)
[key: string]: any;
}Usage Examples:
import { TextField } from '@mui/material';
// Material-UI integration
function MaterialPhoneInput() {
return (
<InputMask mask="+1 (999) 999-9999" value={value} onChange={onChange}>
{(inputProps) => (
<TextField
{...inputProps}
label="Phone Number"
variant="outlined"
/>
)}
</InputMask>
);
}
// Custom styled input
function StyledInput() {
return (
<InputMask mask="99/99/9999">
{(inputProps) => (
<input
{...inputProps}
className="custom-date-input"
style={{ border: '2px solid blue' }}
/>
)}
</InputMask>
);
}Access the underlying input DOM node for focus, selection, and other operations.
/**
* Input reference callback
* @param ref - HTML input element reference
*/
type InputRefCallback = (ref: HTMLInputElement | null) => void;Usage Examples:
function FocusableInput() {
const inputRef = React.useRef(null);
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<InputMask
mask="999-999-9999"
inputRef={(ref) => { inputRef.current = ref; }}
/>
<button onClick={focusInput}>Focus Input</button>
</div>
);
}/**
* Main InputMask component props interface
*/
interface InputMaskProps {
mask?: string;
maskChar?: string | null;
formatChars?: { [key: string]: string };
alwaysShowMask?: boolean;
inputRef?: (ref: HTMLInputElement | null) => void;
beforeMaskedValueChange?: BeforeMaskedValueChangeHandler;
children?: (inputProps: any) => React.ReactElement;
value?: string;
defaultValue?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
onPaste?: (event: React.ClipboardEvent<HTMLInputElement>) => void;
onMouseDown?: (event: React.MouseEvent<HTMLInputElement>) => void;
disabled?: boolean;
readOnly?: boolean;
placeholder?: string;
className?: string;
style?: React.CSSProperties;
[key: string]: any;
}
/**
* State object for beforeMaskedValueChange callback
*/
interface MaskedValueChangeState {
value: string;
selection: { start: number; end: number } | null;
}
/**
* Mask configuration options
*/
interface MaskOptions {
mask: string;
maskChar: string;
alwaysShowMask: boolean;
formatChars: { [key: string]: string };
permanents: number[];
}
/**
* Handler for advanced masking logic
*/
type BeforeMaskedValueChangeHandler = (
newState: MaskedValueChangeState,
oldState: MaskedValueChangeState,
userInput: string | null,
maskOptions: MaskOptions
) => MaskedValueChangeState;
/**
* Default format character definitions
*/
interface DefaultFormatChars {
'9': '[0-9]';
'a': '[A-Za-z]';
'*': '[A-Za-z0-9]';
}// US phone number
<InputMask mask="+1 (999) 999-9999" />
// International format
<InputMask mask="+99 999 999 9999" />
// Simple format
<InputMask mask="999-999-9999" />// US date format
<InputMask mask="99/99/9999" placeholder="MM/DD/YYYY" />
// European date format
<InputMask mask="99.99.9999" placeholder="DD.MM.YYYY" />
// ISO date format
<InputMask mask="9999-99-99" placeholder="YYYY-MM-DD" />// Social Security Number
<InputMask mask="999-99-9999" />
// Credit Card
<InputMask mask="9999 9999 9999 9999" />
// ZIP Code
<InputMask mask="99999" />
// ZIP+4
<InputMask mask="99999-9999" />// License plate (letters and numbers)
<InputMask mask="aaa-999" />
// Product code
<InputMask mask="***-***-***" />
// Custom format with specific characters
<InputMask
mask="HH:MM"
formatChars={{
'H': '[0-2]',
'M': '[0-5]'
}}
/>