Quickstart | Examples | Demo
- Support custom masks and mask rules to define which characters are allowed.
- Can generate different masks based on the current value.
- Preserve the cursor position when the value is changed, or when a new value (or part of it) is pasted, even when the mask changes.
- Extensible, allowing the use of the generic hook by different components.
- Default hook for web (DOM) components, as well as an implementation of the react-dom input component that uses the hook.
- Function createDefaultMaskGenerator to easily create a mask generator that generates a single mask (independent of the value).
npm install react-hook-mask
import React from 'react';
import { MaskedInput, createDefaultMaskGenerator } from 'react-hook-mask';
const maskGenerator = createDefaultMaskGenerator('999 999 9999');
const Quickstart = () => {
const [value, setValue] = React.useState('');
return (
<div>
<MaskedInput
maskGenerator={maskGenerator}
value={value}
onChange={setValue}
/>
<div>Value (no mask):</div>
<div>{value}</div>
</div>
);
};
export default Quickstart;
You can define a boolean property keepMask
with the value true
to preserve the mask in the value provided (defined with setValue
).
Define a map with custom rules for characters in the mask that must satisfy a regex provided. Characters in the mask not present in the rules are seen as static mask characters (as opposed to user provided characters), and will be included automatically in the display value.
import React from 'react';
import { MaskedInput } from 'react-hook-mask';
const MY_RULES = new Map([
['C', /[A-Za-z]/],
['N', /\d/],
]);
const createMaskGenerator = (mask) => ({
rules: MY_RULES,
generateMask: () => mask,
});
const maskGenerator = createMaskGenerator('CCC-NNNN');
const CustomRules = () => {
const [value, setValue] = React.useState('');
return (
<div>
<MaskedInput
maskGenerator={maskGenerator}
value={value}
onChange={setValue}
/>
<div>Value (no mask):</div>
<div>{value}</div>
</div>
);
};
export default CustomRules;
A different mask can be defined based on the current mask value. The map DEFAULT_MASK_RULES
can be used as the default rules, but custom rules can be defined too. A transform
optional function can be used to transform the string if needed (in the example below, instead of blocking lowercase letters, they are converted to uppercase).
import React from 'react';
import { MaskedInput, DEFAULT_MASK_RULES } from 'react-hook-mask';
const maskGenerator = {
rules: DEFAULT_MASK_RULES,
generateMask: (value) =>
(value?.replaceAll('-', '').length ?? 0) <= 10
? 'AAA-AAA-AAAA'
: 'AAA-AAA-AAA-AAAA',
transform: (v) => v?.toUpperCase(),
};
const DynamicMask = () => {
const [value, setValue] = React.useState('');
return (
<div>
<MaskedInput
maskGenerator={maskGenerator}
value={value}
onChange={setValue}
/>
<div>Value (no mask):</div>
<div>{value}</div>
</div>
);
};
export default DynamicMask;
Use any mask in a custom DOM component (as long it behaves as an HTML input).
import React from 'react';
import { useWebMask } from 'react-hook-mask';
import MyInput from './my-input';
const CustomDOMComponent = React.forwardRef(
(
{
maskGenerator,
value: outerValue,
onChange: onChangeOuter,
keepMask,
...otherProps
},
outerRef,
) => {
const { value, onChange, ref } = useWebMask({
maskGenerator,
value: outerValue,
onChange: onChangeOuter,
keepMask,
ref: outerRef,
});
// The properties myValue, myOnChange and myRef are just examples
return (
<MyInput
{...otherProps}
myValue={value ?? ''}
myOnChange={onChange}
myRef={ref}
/>
);
},
);
export default CustomDOMComponent;
You can see the default MaskedInput component provided by this package as a reference.
Extend the hook to be used by a custom component (or several components, as long as the way to get and to change the cursor position is the same for those components).
The only requirement for the creation of a custom hook is that the input component must have a way to retrieve and to modify the cursor position (in the example below, the read-write property myPosition
was used as an example).
import React from 'react';
import { useRefMask } from 'react-hook-mask';
export const useMyMask = ({
maskGenerator,
value,
onChange,
keepMask,
ref: outerRef,
}) => {
const getCursorPosition = React.useCallback((el) => {
const cursorPosition = el?.myPosition ?? 0;
return cursorPosition;
}, []);
const setCursorPosition = React.useCallback((cursorPosition, el) => {
if (el) {
el.myPosition = cursorPosition;
}
}, []);
const { displayValue, setDisplayValue, ref } = useRefMask({
value,
maskGenerator,
getCursorPosition,
setCursorPosition,
onChange,
keepMask,
ref: outerRef,
});
return { value: displayValue, onChange: setDisplayValue, ref };
};
The hook useRefMask wraps the generic useMask hook and was created to allow the use of the component ref
even if an external ref
is received without having to add boilerplate to handle this case.
You can see the useWebMask and useNativeMask hooks provided by this package as a reference.
The utilitarian function getCurrencyMaskGenerator
can create a mask generator that allows the value be displayed as a currency.
import React from 'react';
import { MaskedInput, getCurrencyMaskGenerator } from 'react-hook-mask';
const maskGenerator = getCurrencyMaskGenerator({
prefix: '$ ',
thousandSeparator: '.',
centsSeparator: ',',
});
export const CurrencyMaskedInput = () => {
const [value, setValue] = React.useState('');
return (
<div>
<MaskedInput
maskGenerator={maskGenerator}
value={value}
onChange={setValue}
/>
<div>
Mask: {value ? maskGenerator.generateMask(value) : undefined}
</div>
<div>Value (no mask):</div>
{value ? <div>{value}</div> : undefined}
</div>
);
};
You can define different currencies (like US$
, R$
, €
), using the prefix, or leave it empty/undefined, if you want only the numeric value.
You can use different separators for thousands, like
, .
, or leave it empty/undefined, if you don't want separators.
You can use different symbols for the cents (decimal) separators, like .
, ,
, or leave it empty/undefined, if the currency has no cents (like yen and won).
In the example above, for an input of 123456789
, you would see $ 1.234.567,89
as the output (the displayed value).
Sometimes you just want to show a masked value as a string. In this case, instead of using an input component, you can just call the mask
function available in this package:
import { mask } from 'react-hook-mask';
const value = '12345678901';
const Component = () => (
<div>
<div>Value: {value}</div>
<div>Masked: {mask(value, maskGenerator)}</div>
</div>
);
If performance is a concern, the masked value can be memoized:
import { mask } from 'react-hook-mask';
const value = '12345678901';
const Component = () => {
const maskedValue = React.useMemo(
() => mask(value, maskGenerator),
[value],
);
return (
<div>
<div>Value: {value}</div>
<div>Masked: {maskedValue}</div>
</div>
);
};
You can use the hook useNativeMask
instead of having to create a custom react-native hook using the lower level useRefMask
hook.
This hook is similar to the useWebMask
hook, except that it's to be used in a react-native TextInput
(or compatible) component.
import React from 'react';
import { useNativeMask } from 'react-hook-mask';
import { Platform, TextInput } from 'react-native';
const MaskedInput = React.forwardRef(
(
{ maskGenerator, value: outerValue, onChange, keepMask, ...otherProps },
outerRef,
) => {
const { ref, value, onChangeText, onSelectionChange } = useNativeMask({
maskGenerator,
value: outerValue,
onChange,
keepMask,
waitToUpdateCursor: Platform.OS === 'ios',
ref: outerRef,
});
return (
<TextInput
{...otherProps}
ref={ref}
value={value}
onChangeText={onChangeText}
onSelectionChange={onSelectionChange}
/>
);
},
);
export default MaskedInput;
Define the property waitToUpdateCursor
as true
on iOS to delay the cursor position update for some milliseconds, otherwise the cursor may end up in a wrong position when changing the value of the input.
The native component can be used in the same way as any other mask component, as shown previously.