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 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-intl-phone-number

v0.2.0

Published

Framework-agnostic, antd-free international phone number input for React. E.164 in/out, searchable flag/country-code dropdown, configurable validation levels (strict / mobile-strict / loose), modern themeable CSS, i18n via a messages object or your own t(

Downloads

272

Readme

react-intl-phone-number

A framework-agnostic, Ant-Design-free international phone number input for React.

  • 🌍 Searchable country dropdown — pick by flag or by calling code (+60, +66, …).
  • 📞 E.164 in / out, powered by google-libphonenumber.
  • 🎚️ Configurable, required validationLevel: strict / mobile-strict / loose.
  • 🎨 Modern, themeable CSS — every part is a CSS variable and every node accepts a classNames override, so external utilities (e.g. Tailwind) win without !important.
  • 🌐 i18n via a messages object (English defaults) and/or your own t() function.
  • 🧩 Framework-agnostic core (react-intl-phone-number/core) — validate without React.

Preview

| Leveled validation | Country / code picker | Format hints | | :---: | :---: | :---: | | Validation states: valid, invalid (TOO_LONG), required | Searchable flag + calling-code dropdown | Info tooltip showing accepted mobile/landline formats |

Install

npm i react-intl-phone-number

Peer dependencies (declared, so most package managers install them automatically):

npm i react react-dom google-libphonenumber

google-libphonenumber ships full metadata (~hundreds of KB). It is a peer dependency so it is installed once and shared with the rest of your app rather than bundled into this package.

Quick start

import { useState } from "react"
import PhoneNumberInput from "react-intl-phone-number"
import "react-intl-phone-number/styles.css" // import once, anywhere in your app

function Example() {
  const [phone, setPhone] = useState("") // E.164, e.g. "+66948383493"
  return (
    <PhoneNumberInput
      value={phone}
      onChange={setPhone}
      defaultCountry="MY"
      validationLevel="mobile-strict"
    />
  )
}

Using CommonJS / require? The component is the default (or the named export):

const { PhoneNumberInput, validatePhoneNumber } = require("react-intl-phone-number")

Validation levels

validationLevel is required — you decide how strict per use case.

| level | mobile | landline | | --- | --- | --- | | strict | pattern-valid (isValidNumber) | pattern-valid | | mobile-strict | pattern-valid | length-checked only | | loose | length-checked only | length-checked only |

Note on mobile-strict: in countries where mobile and landline share length ranges (US / MY / TH …), a possible-length number is accepted via the landline "length-only" branch, so mobile-strict only rejects a number that is mobile-length-possible, mobile-pattern-invalid, and not a possible landline length. The mobile-vs-landline distinction is only observable in regions whose mobile and landline lengths are disjoint.

Built-in error display

By default the component renders no error (you wire validation yourself). Opt in with showError to render the validation message under the input after blur, or pass an explicit error node (e.g. your "required" message):

// auto: shows the reason message (per validationLevel) once the field is blurred
<PhoneNumberInput validationLevel="strict" showError />

// explicit: you control the message (overrides the built-in one)
<PhoneNumberInput validationLevel="strict" error={isRequired ? "Phone is required" : undefined} />

The control gets a red border and the input gets aria-invalid; the error text is themeable via --ripn-error-color and classNames.error. Error wording comes from the core single source (phoneReasonMessage) and bridges to your t when provided.

i18n

Both are optional; built-in English is used otherwise.

// (a) messages object — override any subset of strings
<PhoneNumberInput
  validationLevel="strict"
  messages={{ zeroHint: "Cannot start with 0", ruleHintTitle: "Accepted formats:" }}
/>

// (b) translate function (e.g. react-i18next) — wins over messages when both set.
// Keyed by Label_PhoneNumber_* so existing locale files work unchanged.
<PhoneNumberInput validationLevel="strict" t={(key, vars) => i18n.t(key, vars)} />

messages covers the component-visible strings (placeholder, zero hint, format hints, number-type labels). Validation error wording lives in the core single source (phoneReasonMessage / validatePhoneNumber().message) and is shown via showError / error; pass t to translate it through your existing Label_PhoneNumber_* keys.

Props

| prop | type | notes | | --- | --- | --- | | value | string | controlled E.164 | | onChange | (value: string) => void | emits E.164 when valid, else a truthy +<cc><digits> partial | | onBlur | () => void | | | defaultCountry | CountryCode | changing it adopts the country and clears the number | | disabled | boolean | | | validationLevel | 'strict' \| 'mobile-strict' \| 'loose' | required | | hintTypes | PhoneNumberKind[] | info-icon tooltip types; default mobile + landline | | messages | Partial<Messages> | i18n overrides | | t | (key, vars?) => string | translate fn; wins over messages | | onValidityChange | (isValid: boolean) => void | computed only when provided | | showError | boolean | render the built-in validation error under the input after blur | | error | ReactNode | explicit error to show (overrides the built-in one; e.g. "required") | | className / style | | on the root | | classNames | Partial<Record<'root'\|'group'\|'select'\|'dropdown'\|'option'\|'input'\|'tooltip'\|'infoIcon'\|'error', string>> | per-part overrides (drop in Tailwind classes) | | id / name / aria-label / inputRef | | form / a11y plumbing |

Theming

Override the CSS variables on .ripn-root (or globally), or pass classNames:

.ripn-root {
  --ripn-radius: 12px;
  --ripn-border-color-focus: #16a34a;
  --ripn-error-color: #dc2626;
}

Key tokens: --ripn-border-color, --ripn-border-color-hover, --ripn-border-color-focus, --ripn-ring, --ripn-radius, --ripn-height, --ripn-error-color, --ripn-warning-bg, --ripn-option-selected-bg, --ripn-tooltip-bg. Prefer skipping styles.css entirely? Pass your own classNames.* for full control.

Framework-agnostic core

import {
  validatePhoneNumber,
  phoneReasonMessage,
  getPhoneNumberError,
  phoneReasonI18nKey,
  toE164,
} from "react-intl-phone-number/core"

// Every result carries the google-libphonenumber-derived `reason` CODE *and* a
// default English `message` — display it directly, or map the code yourself.
validatePhoneNumber("+66948383493", { level: "strict" })
// → { valid: true,  reason: null,        message: null }
validatePhoneNumber("+6612", { level: "strict" })
// → { valid: false, reason: "TOO_SHORT", message: "Phone number is too short." }

getPhoneNumberError("+6612") //  "TOO_SHORT"  (just the code; level defaults to "strict")
phoneReasonMessage("TOO_LONG") //  "Phone number is too long."
phoneReasonI18nKey("TOO_SHORT") //  "Label_PhoneNumber_TooShort"  (for your own i18n)

reason is one of INVALID_COUNTRY_CODE / NOT_A_NUMBER / TOO_SHORT / TOO_LONG / INVALID_LENGTH / NOT_VALID (mirrors google-libphonenumber's classification). No React, no DOM — usable in form validators or on a server.

License

MIT