```
```tsx
import { memo } from 'react';
import { withTanStackFormMask } from 'use-mask-input';
const MaskedField = memo(function MaskedField({ inputProps }) {
const maskedProps = withTanStackFormMask(inputProps, '(99) 99999-9999');
return ;
});
```
---
### Ant Design Hooks
Import from `use-mask-input/antd`. These hooks unwrap Ant Design's `InputRef` structure automatically and accept `InputRef | null` instead of `HTMLElement | null`.
```bash
npm install use-mask-input antd
```
---
#### `useMaskInputAntd`
```ts
import { useMaskInputAntd } from 'use-mask-input/antd';
function useMaskInputAntd(props: {
mask: Mask;
register?: (element: HTMLElement) => void;
options?: Options;
}): (input: InputRef | null) => void
```
```tsx
import { Input, Form, Button } from 'antd';
import { useMaskInputAntd } from 'use-mask-input/antd';
// Basic usage
function PhoneInput() {
const ref = useMaskInputAntd({ mask: '(99) 99999-9999' });
return ;
}
// With Ant Design Form
function AntdForm() {
const [form] = Form.useForm();
const phoneMaskRef = useMaskInputAntd({ mask: '(999) 999-9999' });
const emailMaskRef = useMaskInputAntd({ mask: 'email' });
const currencyMaskRef = useMaskInputAntd({
mask: 'currency',
options: { prefix: '$ ', radixPoint: '.', digits: 2, groupSeparator: ',', rightAlign: false },
});
return (
);
}
```
Watch live values with `Form.useWatch`:
```tsx
function WatchExample() {
const [form] = Form.useForm();
const values = Form.useWatch([], form);
const maskRef = useMaskInputAntd({ mask: 'email' });
return (
{JSON.stringify(values, null, 2)}
);
}
```
---
#### `useHookFormMaskAntd`
Combines React Hook Form and Ant Design.
```ts
import { useHookFormMaskAntd } from 'use-mask-input/antd';
function useHookFormMaskAntd(
registerFn: UseFormRegister
): (
fieldName: Path,
mask: Mask,
options?: RegisterOptions & Options
) => UseHookFormMaskAntdReturn
```
```tsx
import { Input } from 'antd';
import { useForm } from 'react-hook-form';
import { useHookFormMaskAntd } from 'use-mask-input/antd';
interface FormData {
phone: string;
email: string;
}
function MyForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const registerWithMask = useHookFormMaskAntd(register);
return (
);
}
```
---
## Mask Types Deep Dive
### Static Mask
Fixed pattern that doesn't change during input. Any position is either a pattern character (`9`, `a`, `A`, `*`, `#`) or a literal.
```tsx
// Phone
useMaskInput({ mask: '(99) 99999-9999' })
// Date
useMaskInput({ mask: '99/99/9999' })
// Credit card
useMaskInput({ mask: '9999 9999 9999 9999' })
// Brazilian ZIP code
useMaskInput({ mask: '99999-999' })
// License plate (old Brazilian format: letters + digits)
useMaskInput({ mask: 'AAA-9999' })
// Custom ID
useMaskInput({ mask: 'ID: 999-AAA' })
```
Tips:
- `A` forces uppercase letters; `a` accepts any case
- Literal characters are always shown at the corresponding position
### Dynamic Mask
Uses curly-brace repetition syntax for variable-length inputs.
```tsx
// Exactly 4 digits after letters
useMaskInput({ mask: 'aa-9{4}' })
// 1 to 10 digits
useMaskInput({ mask: '9{1,10}' })
// 2 letters followed by 4–8 digits
useMaskInput({ mask: 'A{2}-9{4,8}' })
// Email (manual dynamic pattern)
useMaskInput({ mask: '*{1,20}[.*{1,20}][.*{1,20}][.*{1,20}]@*{1,20}[.*{2,6}][.*{1,2}]' })
```
JIT masking — only shows the mask characters as the user types:
```tsx
useMaskInput({
mask: '9{4|1}',
options: { jitMasking: true },
})
```
### Optional Mask
Sections in square brackets are optional.
```tsx
// Phone with optional 9th digit (mobile vs landline)
useMaskInput({ mask: '(99) [9]9999-9999' })
// Phone with optional area code
useMaskInput({ mask: '[99] 9999-9999' })
// International phone: optional country code + optional area code
useMaskInput({ mask: '[+99] [9]9999-9999' })
// Credit card with optional groups
useMaskInput({ mask: '9999[ 9999][ 9999][ 9999]' })
```
### Alternator Mask
Multiple valid patterns separated by `|` or as an array. The engine selects the pattern that matches the current input.
```tsx
// Landline or mobile
useMaskInput({ mask: '(99) 9999-9999|(99) 99999-9999' })
// Array syntax (recommended for more than 2 patterns)
useMaskInput({ mask: ['9999-9999', '99999-9999'] })
// Credit card types
useMaskInput({ mask: ['9999 9999 9999 9999', '9999 999999 99999'] })
// Date separators
useMaskInput({ mask: '99/99/9999|99-99-9999|99.99.9999' })
// Old and Mercosul license plates
useMaskInput({ mask: ['AAA-9999', 'AAA9A99'] })
```
### Preprocessing Mask (function)
Pass a function to compute the mask pattern at evaluation time. Useful for conditional logic, country-specific formats, or patterns fetched from an API.
```tsx
// Country-specific phone
function CountryPhoneInput({ country }: { country: string }) {
const ref = useMaskInput({
mask: function () {
const formats: Record = {
US: '(999) 999-9999',
BR: '(99) 99999-9999',
UK: '9999 999 999',
DE: '9999 9999999',
};
return formats[country] || '9999-9999';
},
});
return ;
}
// Input type selector
useMaskInput({
mask: function () {
if (inputType === 'phone') return ['(99) 9999-9999', '(99) 99999-9999'];
if (inputType === 'email') return 'email';
if (inputType === 'postal') return '99999-999';
return '9999-9999';
},
})
```
---
## Options Reference
The `options` parameter maps to Inputmask configuration. Commonly used options:
| Option | Type | Default | Description |
|---|---|---|---|
| `placeholder` | `string` | `"_"` | Character shown in unfilled positions |
| `autoUnmask` | `boolean` | `false` | If `true`, the input value returns raw unmasked data (without literals) |
| `prefix` | `string` | `""` | Text prepended to the input, always present |
| `suffix` | `string` | `""` | Text appended to the input, always present |
| `radixPoint` | `string` | `"."` | Decimal separator character |
| `groupSeparator` | `string` | `""` | Thousands separator character |
| `digits` | `number` | `2` | Number of decimal digits (numeric/currency aliases) |
| `rightAlign` | `boolean` | `true` | Right-align input text |
| `inputFormat` | `string` | — | Date input format for `datetime` alias (e.g. `"dd/mm/yyyy"`) |
| `outputFormat` | `string` | — | Date output format for `datetime` alias (e.g. `"yyyy-mm-dd"`) |
| `min` | `number` | — | Minimum allowed value (numeric aliases) |
| `max` | `number` | — | Maximum allowed value (numeric aliases) |
| `jitMasking` | `boolean` | `false` | Show mask characters only as the user types (Just-In-Time) |
| `removeMaskOnSubmit` | `boolean` | `false` | Remove mask characters when the form is submitted |
| `clearMaskOnLostFocus` | `boolean` | `true` | Clear incomplete mask when field loses focus |
| `showMaskOnHover` | `boolean` | `true` | Show mask on hover |
| `showMaskOnFocus` | `boolean` | `true` | Show mask on focus |
For the full option list, see the [Inputmask documentation](https://robinherbots.github.io/Inputmask/).
---
## TypeScript Types
```ts
type Mask =
| 'datetime' | 'email' | 'numeric' | 'currency'
| 'decimal' | 'integer' | 'percentage' | 'url'
| 'ip' | 'mac' | 'ssn' | 'brl-currency'
| 'cpf' | 'cnpj' | 'br-bank-account' | 'br-bank-agency'
| (string & {}) // custom pattern like '(99) 99999-9999'
| (string[] & {}) // alternator as array
| (() => string | string[]) // preprocessing function
| null; // no mask applied
type Input = HTMLInputElement | HTMLTextAreaElement | HTMLElement;
interface TanStackFormInputProps {
name?: string;
ref?: RefCallback;
[key: string]: unknown;
}
type UseTanStackFormMaskReturn =
Omit & {
ref: RefCallback;
prevRef: RefCallback | undefined;
};
```
---
## API Selection Guide
| Situation | Use |
|---|---|
| Plain React `` | `useMaskInput` |
| React Hook Form | `useHookFormMask` |
| TanStack Form | `useTanStackFormMask` |
| Ant Design `` | `useMaskInputAntd` (from `use-mask-input/antd`) |
| Ant Design + React Hook Form | `useHookFormMaskAntd` (from `use-mask-input/antd`) |
| Function-based (no hooks) | `withMask` + `React.memo` |
| Function-based + React Hook Form | `withHookFormMask` + `React.memo` |
| Function-based + TanStack Form | `withTanStackFormMask` + `React.memo` |
---
## Important Caveats
1. **`withMask`, `withHookFormMask`, `withTanStackFormMask` are not hooks.** They do not have internal memoization. Always wrap the component using them with `React.memo`. Without it, every parent re-render creates a new ref callback, which causes the mask to detach and re-attach (flickering, cursor loss).
2. **React Hook Form `reset()` edge case.** When `reset()` is called, React Hook Form updates the form state but does not always update the DOM input value. `useHookFormMask` handles this automatically using `useLayoutEffect`. Use it instead of `withHookFormMask` whenever you call `reset()`.
3. **SSR.** All DOM operations are guarded by a server-side rendering check. The library is safe to import in Next.js and other SSR environments.
4. **`autoUnmask` vs masked values.** By default, `input.value` contains the masked string including literals (e.g. `(11) 99999-8888`). Set `options.autoUnmask: true` to get only the raw digits/characters without any mask literals.
5. **Ant Design `InputRef`.** Ant Design wraps the native `` inside an `InputRef` object. The `useMaskInputAntd` and `useHookFormMaskAntd` hooks unwrap it automatically. Do not use standard `useMaskInput` with Ant Design's `` — use the `/antd` subpackage hooks instead.
---
## Exports
Main package (`use-mask-input`):
- `useMaskInput`
- `useHookFormMask`
- `useTanStackFormMask`
- `withMask`
- `withHookFormMask`
- `withTanStackFormMask`
Ant Design subpackage (`use-mask-input/antd`):
- `useMaskInputAntd`
- `useHookFormMaskAntd`
---
## Links
- npm: https://www.npmjs.com/package/use-mask-input
- GitHub: https://github.com/eduardoborges/use-mask-input
- Documentation: https://use-mask-input.eduardoborges.dev
- Inputmask options reference: https://robinherbots.github.io/Inputmask/