npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-hook-mask

v1.1.18

Published

React masked input component and hooks

Downloads

15,754

Readme

React Masked Input and Hooks

npm downloads npm npm

Features

  • 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).

Install

npm install react-hook-mask

Quickstart

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 custom rules

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;

Define a dynamic mask

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;

Custom DOM component

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.

Custom mask hook

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.

Currency mask

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).

Show the mask as a string

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>
    );
};

React Native

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.