The react-phone-input-2 npm package is a case study in dependency risk hiding behind popularity: hundreds of thousands of weekly downloads, embedded in countless signup flows, and no new release since version 2.15.1 shipped in 2021. Form input packages are easy to adopt because the demo looks great, but they sit directly on your data-entry path where validation, injection, and data quality problems begin. This post reviews three packages developers reach for constantly and gives an honest verdict on each.
Forms are an attack surface, not just UI
Everything a form component does happens at the trust boundary. It receives raw user input, applies client-side "validation" that attackers can bypass entirely, and shapes the payload your API receives. Three properties matter when vetting a form package:
- Maintenance status — validation logic rots as formats change (phone numbering plans, country subdivisions, email standards).
- Data handling — does the package normalize, mask, or transform input in ways your server-side validation must mirror?
- Dependency weight — form widgets that embed large datasets (flags, country lists) bloat bundles and pin stale data.
react-phone-input-2: popular and frozen
react-phone-input-2 renders an international phone input with country flags, dial codes, and masking. Its appeal is obvious, and its weekly download count remains enormous. Its release history is the problem: the last published version, 2.15.1, arrived in 2021, and the project shows the classic signs of an inactive package with usage momentum.
Why a frozen phone input is worse than a frozen tooltip:
- Numbering plans change. Countries add prefixes and renumber ranges. A masking library frozen for years will mis-validate real numbers, silently rejecting legitimate users or accepting malformed input your backend then trusts.
- The dial-code dataset is baked in. No releases means no data updates.
- Open issues stay open. Bug reports about masking edge cases accumulate with no path to a fix except forking.
If you keep react-phone-input-2 in production, treat its output as untrusted display formatting only, and re-validate numbers server-side with a maintained library (Google's libphonenumber lineage is the usual choice). Better: migrate to an actively maintained phone input or build a thin wrapper over a maintained parsing library so you control the UI without inheriting a frozen dataset. Our post on abandoned open source project risks covers how these packages decay and what takeover risk looks like.
npm react-hook-form: the well-run counterexample
The npm react-hook-form package is what healthy looks like. It is actively maintained, has minimal dependencies, and its design keeps form state in refs rather than re-rendering on every keystroke.
From a security review perspective, the npm react hook form story is mostly positive with two operational notes:
- Client-side rules are UX, not security. react-hook-form's resolver ecosystem (zod, yup) makes it trivial to write rich validation schemas. Reuse the same schema server-side where possible; if your API cannot share the schema, treat every rule as needing a server-side twin. Client validation exists to help users, not to stop attackers.
- Watch what you register. Spreading entire form payloads into API calls (
api.post('/user', data)) invites mass-assignment bugs when the form grows a field the backend did not expect. Whitelist fields explicitly at the boundary.
Verdict: adopt freely, keep it current, and let its release notes drive your update cadence rather than pinning and forgetting.
country state city npm: a dataset wearing a package costume
The country state city npm package (country-state-city) ships a static dataset of countries, subdivisions, and cities with lookup helpers. Packages like this are less "code risk" and more "data liability":
- Size. Embedding a global city list adds megabytes to a client bundle. If the dropdown lives on one signup page, lazy-load it or move the lookup to an API endpoint.
- Staleness and disputes. Administrative divisions change, and country/territory lists are politically sensitive. A stale dataset can misroute tax, shipping, or compliance logic. Know the dataset's provenance and update cadence before wiring it into anything that feeds billing.
- Validation mismatch. If the client offers subdivision options from one dataset and your backend validates against another (or none), you get records that pass the form but fail downstream systems. Pick one source of truth.
Verdict: fine as a convenience for low-stakes UI. For anything feeding compliance, tax, or shipping logic, serve the reference data from your backend where you can patch it without an app release.
Vetting workflow for form-layer packages
A concrete pre-adoption checklist that takes about fifteen minutes:
npm view react-phone-input-2 time.modified
npm view react-phone-input-2 maintainers
npm ls --all | grep -i phone
- Check the last publish date and release cadence (
npm view <pkg> time). - Read the open issues for words like "abandoned", "maintainer", "fork".
- Count transitive dependencies and embedded datasets.
- Confirm every validation rule the widget applies has a server-side equivalent.
- Add the package to continuous scanning so new advisories surface without anyone re-auditing manually. An SCA platform like Safeguard flags inactive lineages and known advisories across the whole tree, which is exactly the signal frozen-but-popular packages evade in casual review.
For the broader hygiene layer around lockfiles, scripts, and publish integrity, see npm security best practices.
FAQ
Is react-phone-input-2 still maintained?
No. Its last release, 2.15.1, was published in 2021, and activity since has been minimal despite continued heavy downloads. Treat it as frozen: keep server-side phone validation authoritative, and plan a migration to a maintained input or a wrapper over a maintained parsing library.
Is react-hook-form safe for production?
Yes, with the standard caveat that client-side validation is user experience, not enforcement. The package is actively maintained and dependency-light. Mirror its validation rules on the server and whitelist fields before persisting form payloads.
Should I bundle country-state-city data in my frontend?
Only for low-stakes UI. The dataset is large and goes stale, and administrative divisions change. For tax, shipping, or compliance flows, serve reference data from your backend so updates do not require a client release.
How do I detect frozen packages in an existing project?
Query publish dates in bulk (npm view <pkg> time.modified) or use a composition analysis tool that reports maintenance signals alongside vulnerabilities. Anything popular with multi-year silence deserves a replacement plan before an advisory forces one.