react-imask

React bindings for IMask: a masked input component, a useIMask hook, and an IMaskMixin HOC for wrapping any existing input.

Library
npm
v7.6.1
5,185stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
51/100Fair
Development Activity0
Maintenance44
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
60/100Good
Architecture75
Code Quality45
Innovation55
Learning Curve65

react-imask provides official React bindings for IMask, one of the most widely used input-masking engines in the JavaScript ecosystem. It ships three integration patterns from a single package: a ready-to-use IMaskInput component, a useIMask hook for wiring mask state into custom-rendered UI, and an IMaskMixin higher-order component for adding masking to an already-existing input-like component (a styled-components input, a design-system TextField, etc.) without rewriting it.

Under the hood it delegates all masking logic to the core imask package (pattern, numeric, date, dynamic, and enum masks) and focuses purely on React lifecycle concerns: creating and destroying the underlying mask instance alongside the component, keeping value/unmaskedValue/typedValue in sync with controlled component state, and re-emitting IMask’s accept/complete events as onAccept/onComplete props.

What You Get

  • IMaskInput component - a drop-in <input> replacement that applies a mask and exposes onAccept/onComplete callbacks plus a maskRef to the underlying IMask instance.
  • useIMask hook - wires mask state (value, unmaskedValue, typedValue and their setters) into any custom-rendered input via a ref, for full control over markup.
  • IMaskMixin HOC - wraps an existing component (a styled-components input, a design-system field, etc.) with masking behavior via an inputRef prop, without touching its implementation.
  • Full re-export of the imask core API - IMask and all its Masked* classes are re-exported, so consumers don’t need a second dependency for advanced mask configuration.

Common Use Cases

  • Phone number and date inputs - use pattern masks with IMaskInput to enforce a fixed format as the user types.
  • Currency and numeric fields - use the Number mask with radix/scale/thousandsSeparator options for formatted amount inputs.
  • Masking a design-system input - wrap an existing styled or custom input component with IMaskMixin instead of rewriting it.
  • Custom-rendered masked fields - use useIMask when the input markup can’t be a plain <input> but still needs mask state and callbacks.

Under The Hood

Architecture The package lives inside the imaskjs Lerna monorepo alongside sibling framework bindings (angular-imask, vue-imask, svelte-imask, solid-imask), all wrapping the same core imask engine. Internally, src/index.ts re-exports the core IMask API and three integration surfaces: input.ts (the IMaskInput class-style component, itself built by calling IMaskMixin on a plain <input> renderer), mixin.ts (IMaskMixin, a higher-order component implemented as a legacy React.Component subclass that creates/destroys the mask instance in componentDidMount/componentDidUpdate/componentWillUnmount), and hook.ts (useIMask, a functional hook built from useRef/useState/useEffect that re-implements the same creation, sync, and teardown lifecycle in hooks form). All three funnel through the same IMask(el, opts) factory call and mask.on('accept'|'complete', ...) event wiring, so a change to that core factory contract would ripple through the component, hook, and mixin simultaneously.

Tech Stack Written in TypeScript against the monorepo’s shared tsconfig.json, with prop-types used for runtime prop validation alongside the TypeScript types. The only runtime dependency is the sibling imask workspace package; React itself is a peer dependency accepting anything >=0.14.0. Builds run through tsc --emitDeclarationOnly (for .d.ts output) plus Rollup (@rollup/plugin-babel, -commonjs, -node-resolve, -terser) to produce dual CJS/ESM bundles wired up through a modern exports map, orchestrated across the monorepo via Lerna 8 and npm workspaces.

Code Quality No dedicated test files exist under packages/react-imask/src — the package defines lint and build scripts but no test script, so it’s excluded from the monorepo’s lerna run test --parallel; only the core imask package has a test/ directory. Error handling is implicit rather than explicit (no try/catch blocks; correctness leans on TypeScript’s type system and PropTypes runtime warnings). Naming is consistent and the code makes deliberate use of fairly advanced generic types (ExtractMaskOpts, UnmaskValue, ReactMaskProps) to keep callback value types (value vs unmaskedValue vs typedValue) accurate for the specific mask options passed in. ESLint (with @typescript-eslint and eslint-plugin-tsdoc) is configured at the monorepo root and run via each package’s lint script.

What Makes It Unique The wrapper pattern itself (component + hook + HOC around a masking engine) is a common shape for input-masking libraries. What differentiates it is offering three distinct integration surfaces from one package — covering both the legacy class-based extension pattern (IMaskMixin) and a modern hooks API (useIMask) — combined with the core imask engine’s typed-value support, where typedValue can be returned as a native Date or Number rather than a plain string, a feature most comparable masking wrappers don’t expose.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search