Skip to content

About

react-maskify 라이브러리

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

react-maskify

npm version npm downloads bundle size license CI

Input mask component and hook for React 17, 18 and 19. It accepts the props of react-input-mask v2 and v3, so existing code can switch by changing the import.

Live demo · Migration guide · Changelog

  • No findDOMNode. Works with React 19 and StrictMode. ref works as a prop and through forwardRef.
  • No runtime dependencies. ESM and CJS builds with TypeScript types. About 3 KB min+gzip.
  • Keeps the caret in place when typing, deleting in the middle, pasting and replacing a selection.
  • Waits for IME composition (Korean, Japanese, Chinese) to finish before applying the mask.
  • Renders on the server (Next.js, Remix) without touching window.
  • Korean phone, business number, resident number, card and date presets with validators in react-maskify/presets.

Install

npm i react-maskify

Usage

import InputMask from 'react-maskify';

<InputMask mask="999-9999-9999" placeholder="010-0000-0000" />
<InputMask mask="9999 9999 9999 9999" maskChar={null} />
<InputMask mask="99/99/9999" maskPlaceholder="dd/mm/yyyy" alwaysShowMask />

Mask syntax

char accepts
9 [0-9]
a [A-Za-z]
* [A-Za-z0-9]
\ makes the next character a literal ('+\\9 999' in JS → +9 ___)

Any other character is a literal. formatChars replaces the default rules:

<InputMask mask="#999999" formatChars={{ 9: '[0-9a-fA-F]' }} />

A mask can also be an array of literals and RegExps, or a function that returns a mask for the current value:

<InputMask mask={['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/]} />
<InputMask mask={(value) => (value.startsWith('02') ? '99-9999-9999' : '999-9999-9999')} maskChar={null} />

Controlled

const [phone, setPhone] = useState('');
<InputMask mask="999-9999-9999" value={phone} onChange={(e) => setPhone(e.target.value)} />

e.target.value is the masked value. unmask(value, mask) returns only the typed characters.

Custom input

Both the v2 render function and the v3 single element work:

<InputMask mask="9999-99-99" value={v} onChange={onChange}>
  {(inputProps) => <TextField {...inputProps} />}
</InputMask>

<InputMask mask="9999-99-99" value={v} onChange={onChange}>
  <TextField />
</InputMask>

Put disabled and readOnly on InputMask, not on the child.

react-hook-form

<InputMask mask="999-9999-9999" {...register('phone')} />

<Controller
  name="birth"
  control={control}
  render={({ field }) => <InputMask mask="9999-99-99" maskChar={null} {...field} />}
/>

useMask hook

import { useMask } from 'react-maskify';

function PhoneField() {
  const { inputProps, unmaskedValue } = useMask({ mask: '999-9999-9999', maskChar: null });
  return <input {...inputProps} inputMode="numeric" />;
}

Utilities without React

import { formatWithMask, unmask, isComplete } from 'react-maskify';

formatWithMask('01012345678', '999-9999-9999'); // '010-1234-5678'
formatWithMask('0101', '999-9999-9999', { maskChar: '_' }); // '010-1___-____'
unmask('010-1234-5678', '999-9999-9999'); // '01012345678'
isComplete('010-1234-567', '999-9999-9999'); // false

Korean presets

react-maskify/presets contains plain strings and functions only, so a bundler keeps just what you import (importing isValidBizNoKR alone adds about 200 bytes).

import InputMask from 'react-maskify';
import { phoneMaskKR, isValidBizNoKR, maskRRN, BIZ_NO_KR } from 'react-maskify/presets';

<InputMask mask={phoneMaskKR} maskChar={null} inputMode="numeric" />
<InputMask mask={BIZ_NO_KR} maskChar={null} />

isValidBizNoKR('220-81-62517'); // true
maskRRN('900101-1234567'); // '900101-1******'
export description
phoneMaskKR(value) mask chosen from the digits: 02-123-4567, 02-1234-5678, 031-123-4567, 010-1234-5678, 1588-1234
landlineMaskKR same as phoneMaskKR
formatPhoneKR(value) formats digits with the rules above
MOBILE_KR 999-9999-9999
BIZ_NO_KR 999-99-99999 (business registration number)
RRN_KR 999999-9999999 (resident registration number)
CARD_KR 9999-9999-9999-9999
DATE_KR, DATE_DOT_KR 9999-99-99, 9999.99.99
isValidMobileKR 010 with 11 digits, or 011/016/017/018/019 with 10-11 digits
isValidLandlineKR 02, regional area codes, 070, 15xx/16xx/18xx
isValidBizNoKR length and check digit
isValidRRN(value, { checksum }) length, birth date and gender digit; the legacy check digit only with checksum: true, because numbers issued since October 2020 do not use it
maskRRN(value, { keep, char }) hides the back part for display
isValidCardNumber Luhn check, 13-19 digits
isValidDateKR date that exists on the calendar

The older presets object (presets.phoneKR, presets.card, ...) is still exported from react-maskify.

Props

prop type default notes
mask string | (string | RegExp)[] | (value) => mask falsy renders a plain input
maskChar string | null '_' placeholder for empty slots, null hides them
maskPlaceholder string | null v3 name; one character, or one per mask position. Wins over maskChar
formatChars Record<string, string | RegExp> 9, a, * replaces the defaults
alwaysShowMask boolean false show the mask when empty and not focused
beforeMaskedValueChange (newState, oldState, userInput, maskOptions) => state v2 hook; maskOptions also has slots: (string | RegExp)[], one per mask position
beforeMaskedStateChange ({ previousState, currentState, nextState }) => state v3 hook
inputRef Ref<HTMLInputElement> v2 ref prop
ref Ref<HTMLInputElement> the <input> element
children (props) => element or element custom input

Other props are passed to the <input>.

Migrating from react-input-mask

react-input-mask 2.0.4 was published on 2018-09-01 (3.0.0-alpha.2 on 2020-01-21), and the repository is archived. It calls ReactDOM.findDOMNode, which React 19 removed, so it fails with findDOMNode is not a function.

npm uninstall react-input-mask @types/react-input-mask
npm install react-maskify
npx react-maskify migrate src     # rewrites the imports; add --dry to preview

Before:

import InputMask from 'react-input-mask';

<InputMask mask="99/99/9999" maskChar={null} value={value} onChange={onChange}>
  {(inputProps) => <TextField {...inputProps} />}
</InputMask>

After:

import InputMask from 'react-maskify/compat';

<InputMask mask="99/99/9999" maskChar={null} value={value} onChange={onChange}>
  {(inputProps) => <TextField {...inputProps} />}
</InputMask>
react-input-mask react-maskify status
import InputMask from 'react-input-mask' import InputMask from 'react-maskify/compat' same component
mask string with 9, a, *, \ same same
mask array (v3) same same
formatChars (v2) same same
maskChar (v2) same same, default '_'
maskPlaceholder (v3) same single or full-length string
alwaysShowMask same same
beforeMaskedValueChange (v2) same oldState.selection is null
beforeMaskedStateChange (v3) same same
children function (v2) / element (v3) same both accepted
inputRef (v2) inputRef or ref points to the <input>
ref to class instance ref to <input> instance methods are not available
@types/react-input-mask built in Props, InputState types exported

Behavior differences and more examples are in docs/MIGRATION.md.

한국어 안내

react-input-mask를 쓰던 코드는 import를 react-maskify/compat으로 바꾸면 그대로 동작합니다. npx react-maskify migrate src로 import를 일괄 변경할 수 있습니다.

  • React 17, 18, 19 지원. findDOMNode를 쓰지 않습니다.
  • 한글 IME 조합 중에는 마스크를 적용하지 않고 조합이 끝난 뒤 적용합니다.
  • react-maskify/presets에 휴대폰, 02 포함 지역번호, 사업자등록번호, 주민등록번호(뒷자리 가림), 카드번호, 날짜 마스크와 검증 함수가 있습니다.
import InputMask from 'react-maskify';
import { phoneMaskKR, isValidMobileKR, maskRRN } from 'react-maskify/presets';

<InputMask mask={phoneMaskKR} maskChar={null} placeholder="010-0000-0000" />
isValidMobileKR('010-1234-5678'); // true
maskRRN('900101-1234567'); // '900101-1******'

License

MIT © rjsgud49

About

react-maskify 라이브러리

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages