form-input-mask-field
v1.0.7
Published
A customizable form mask field component built with TypeScript
Maintainers
Readme
FormMaskField Component
A flexible, Material-UI integrated form field component with advanced text masking capabilities and Formik integration.
it is part of https://www.npmjs.com/package/form-input-fields
Features
- Material-UI Integration: Consistent styling with other form components
- Formik Integration: Seamless form state management
- Flexible Masking: Pattern-based input masking with multiple character types
- Uppercase Conversion: Automatic text transformation
- TypeScript Support: Full type safety and IntelliSense
- Clean Value Option: Return masked or unmasked values to form state
Usage
Basic Usage with Formik
import { Formik, Field } from "formik";
import { FormMaskField } from "form-input-mask-field";
<Formik
initialValues={{ phoneNumber: "", gameCode: "" }}
onSubmit={(values) => console.log(values)}
>
<Field
component={FormMaskField}
name="phoneNumber"
label="Phone Number"
mask="(999) 999-9999"
placeholder="Enter phone number"
/>
<Field
component={FormMaskField}
name="gameCode"
label="Game Code"
mask="AAAAA"
toUpperCase={true}
returnCleanValue={true}
/>
</Formik>;Mask Pattern Characters
| Character | Description | Example |
| --------- | ---------------------------- | ------------------------ |
| 9 | Digit (0-9) | 999-99-9999 for SSN |
| A | Letter (a-z, A-Z) | AAA for country code |
| * | Alphanumeric (a-z, A-Z, 0-9) | ***-*** for mixed code |
| a | Lowercase letter (a-z) | aaa for lowercase only |
| Z | Uppercase letter (A-Z) | ZZZ for uppercase only |
| # | Hexadecimal (0-9, A-F, a-f) | ###### for hex color |
| Any other | Literal character | -, (, ), /, etc. |
Props
| Prop | Type | Default | Description |
| ------------------ | ---------- | ------- | -------------------------------------------------------------- |
| mask | string | - | Mask pattern using the characters above |
| placeholderChar | string | '_' | Character shown in placeholder for mask positions |
| toUpperCase | boolean | false | Convert input to uppercase automatically |
| returnCleanValue | boolean | false | Return unmasked value to Formik (true) or masked value (false) |
| showMaskPattern | boolean | false | Show the mask pattern as helper text below the input field |
| showPlaceholder | boolean | false | Show placeholder text with mask pattern in the input field |
| onChange | function | - | Custom change handler with masked, clean, and raw values |
Plus all standard Material-UI TextField props and Formik FieldProps.
Examples
Phone Number
<Field
component={FormMaskField}
name="phone"
label="Phone Number"
mask="(999) 999-9999"
placeholder="(555) 123-4567"
/>Date Input
<Field
component={FormMaskField}
name="date"
label="Date"
mask="99/99/9999"
placeholder="MM/DD/YYYY"
/>Product Code (Uppercase)
<Field
component={FormMaskField}
name="productCode"
label="Product Code"
mask="AAA-999-AAA"
toUpperCase={true}
returnCleanValue={true}
/>Credit Card
<Field
component={FormMaskField}
name="creditCard"
label="Credit Card"
mask="9999 9999 9999 9999"
placeholder="1234 5678 9012 3456"
/>Custom Change Handler
<Field
component={FormMaskField}
name="customField"
label="Custom Field"
mask="999-AAA"
onChange={(event) => {
console.log("Masked value:", event.target.value);
console.log("Clean value:", event.target.cleanValue);
console.log("Raw input:", event.target.rawValue);
}}
/>Show Mask Pattern
<Field
component={FormMaskField}
name="gameCode"
label="Game Code"
mask="AAA-999"
showMaskPattern={true}
toUpperCase={true}
/>
// This will show "Pattern: AAA-999" as helper text below the inputShow Placeholder
<Field
component={FormMaskField}
name="phoneNumber"
label="Phone Number"
mask="(999) 999-9999"
showPlaceholder={true}
/>
// This will show "(___) ___-____" as placeholder text in the input fieldComplete Example Application
Here's a comprehensive example showing how to build a complete form application using FormMaskField with Material-UI and Formik:
App.tsx
import React from "react";
import { Formik, Form, Field, FormikHelpers, FormikErrors } from "formik";
import Container from "@mui/material/Container";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import { Grid } from "@mui/material";
import Paper from "@mui/material/Paper";
import Alert from "@mui/material/Alert";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import { FormMaskField } from "form-input-mask-field";
import "./App.css";
interface FormValues {
phone: string;
date: string;
creditCard: string;
licensePlate: string;
hexColor: string;
customCode: string;
socialSecurity: string;
postalCode: string;
}
interface FormFieldEvent
extends Omit<React.ChangeEvent<HTMLInputElement>, "target"> {
target: HTMLInputElement & {
value: string;
cleanValue: string;
rawValue: string;
name: string;
};
}
// Create a Material-UI theme
const theme = createTheme({
palette: {
primary: {
main: "#1976d2",
},
secondary: {
main: "#dc004e",
},
},
});
// Validation schema
const validateForm = (values: FormValues): FormikErrors<FormValues> => {
const errors: Partial<FormValues> = {};
if (!values.phone) {
errors.phone = "Phone number is required";
} else if (values.phone.replace(/\D/g, "").length < 10) {
errors.phone = "Phone number must be 10 digits";
}
if (!values.date) {
errors.date = "Date is required";
}
if (!values.creditCard) {
errors.creditCard = "Credit card is required";
} else if (values.creditCard.replace(/\D/g, "").length < 16) {
errors.creditCard = "Credit card must be 16 digits";
}
if (!values.licensePlate) {
errors.licensePlate = "License plate is required";
}
return errors;
};
function App() {
const initialValues: FormValues = {
phone: "",
date: "",
creditCard: "",
licensePlate: "",
hexColor: "",
customCode: "",
socialSecurity: "",
postalCode: "",
};
const handleSubmit = (
values: FormValues,
{ setSubmitting }: FormikHelpers<FormValues>
) => {
console.log("Form Values:", values);
setTimeout(() => {
alert("Form submitted! Check console for values.");
setSubmitting(false);
}, 400);
};
const handleCustomChange =
(fieldName: keyof FormValues) => (event: FormFieldEvent) => {
console.log(`${fieldName} changed:`, {
masked: event.target.value,
clean: event.target.cleanValue,
raw: event.target.rawValue,
});
return event;
};
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Container maxWidth="lg" sx={{ py: 4 }}>
<Typography variant="h3" component="h1" gutterBottom align="center">
FormMaskField Demo
</Typography>
<Typography
variant="subtitle1"
align="center"
color="text.secondary"
paragraph
>
A comprehensive example showcasing the FormMaskField component with
various mask patterns and configurations
</Typography>
<Formik
initialValues={initialValues}
validate={validateForm}
onSubmit={handleSubmit}
>
{({ values, isSubmitting, errors, touched }) => (
<Form>
<Grid container spacing={3}>
{/* Basic Masks Section */}
<Grid>
<Paper elevation={2} sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Basic Input Masks
</Typography>
<Grid container spacing={2}>
<Grid>
<Field
name="phone"
component={FormMaskField}
label="Phone Number"
mask="(999) 999-9999"
showMaskPattern={true}
showPlaceholder={true}
helperText="US phone number format"
onChange={handleCustomChange("phone")}
/>
</Grid>
<Grid>
<Field
name="date"
component={FormMaskField}
label="Date"
mask="99/99/9999"
showMaskPattern={true}
showPlaceholder={true}
helperText="MM/DD/YYYY format"
/>
</Grid>
<Grid>
<Field
name="socialSecurity"
component={FormMaskField}
label="Social Security Number"
mask="999-99-9999"
showMaskPattern={true}
showPlaceholder={true}
returnCleanValue={true}
helperText="Clean value returned (no dashes)"
/>
</Grid>
<Grid>
<Field
name="postalCode"
component={FormMaskField}
label="ZIP Code"
mask="99999-9999"
showMaskPattern={true}
showPlaceholder={true}
helperText="US ZIP+4 format"
/>
</Grid>
</Grid>
</Paper>
</Grid>
{/* Advanced Masks Section */}
<Grid>
<Paper elevation={2} sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Advanced Input Masks
</Typography>
<Grid container spacing={2}>
<Grid>
<Field
name="creditCard"
component={FormMaskField}
label="Credit Card Number"
mask="9999-9999-9999-9999"
showMaskPattern={true}
showPlaceholder={true}
helperText="16-digit credit card number"
onChange={handleCustomChange("creditCard")}
/>
</Grid>
<Grid>
<Field
name="licensePlate"
component={FormMaskField}
label="License Plate"
mask="AAA-999"
toUpperCase={true}
showMaskPattern={true}
showPlaceholder={true}
helperText="3 letters + 3 numbers (auto uppercase)"
onChange={handleCustomChange("licensePlate")}
/>
</Grid>
<Grid>
<Field
name="hexColor"
component={FormMaskField}
label="Hex Color Code"
mask="#######"
toUpperCase={true}
showMaskPattern={true}
showPlaceholder={true}
placeholderChar="0"
helperText="6-digit hex color code"
/>
</Grid>
<Grid>
<Field
name="customCode"
component={FormMaskField}
label="Custom Code"
mask="**-999-AA"
toUpperCase={true}
returnCleanValue={true}
showMaskPattern={true}
showPlaceholder={true}
helperText="Alphanumeric + digits + letters"
onChange={handleCustomChange("customCode")}
/>
</Grid>
</Grid>
</Paper>
</Grid>
{/* Current Values Display */}
<Grid>
<Paper elevation={2} sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
Current Form Values
</Typography>
<Box sx={{ mt: 2 }}>
<Typography
variant="body2"
component="pre"
sx={{
backgroundColor: "#f5f5f5",
p: 2,
borderRadius: 1,
overflow: "auto",
fontSize: "0.875rem",
}}
>
{JSON.stringify(values, null, 2)}
</Typography>
</Box>
</Paper>
</Grid>
{/* Form Errors Display */}
{Object.keys(errors).length > 0 &&
Object.keys(touched).length > 0 && (
<Grid>
<Alert severity="error">
<Typography variant="h6" gutterBottom>
Form Validation Errors:
</Typography>
<ul>
{Object.entries(errors).map(
([field, error]) =>
touched[field] && (
<li key={field}>
<strong>{field}:</strong> {error}
</li>
)
)}
</ul>
</Alert>
</Grid>
)}
{/* Submit Button */}
<Grid>
<Box
sx={{ display: "flex", justifyContent: "center", mt: 2 }}
>
<Button
type="submit"
variant="contained"
size="large"
disabled={isSubmitting}
sx={{ minWidth: 200 }}
>
{isSubmitting ? "Submitting..." : "Submit Form"}
</Button>
</Box>
</Grid>
</Grid>
</Form>
)}
</Formik>
</Container>
</ThemeProvider>
);
}
export default App;App.css
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}Key Features Demonstrated
This example application showcases:
- Complete Form Integration: Full Formik integration with validation and error handling
- Multiple Mask Types: Phone numbers, dates, credit cards, license plates, hex colors, and custom codes
- Advanced Features:
- Uppercase conversion (
toUpperCase) - Clean value return (
returnCleanValue) - Custom placeholder characters (
placeholderChar) - Pattern display (
showMaskPattern) - Placeholder display (
showPlaceholder)
- Uppercase conversion (
- Custom Event Handling: onChange handlers that provide masked, clean, and raw values
- Material-UI Integration: Consistent styling with Material-UI theme and components
- Real-time Validation: Form validation with error display
- Live Value Display: Real-time display of current form values
- Responsive Layout: Grid-based responsive layout
Running the Example
To run this example:
- Install dependencies:
npm install - Start the development server:
npm run dev - Open your browser and navigate to the local development URL
- Interact with the form fields to see the masking in action
- Open browser console to see custom onChange event data
The example demonstrates how FormMaskField seamlessly integrates with existing Material-UI and Formik workflows while providing powerful input masking capabilities.
Support
If you like my work, you can support me here:

