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

@input-kit/phone

v0.2.2

Published

Headless phone input with full world country codes, libphonenumber formatting, and TypeScript

Downloads

2,265

Readme

@input-kit/phone

Headless React phone input with a complete world country-code dataset, searchable country selection, and libphonenumber-js powered formatting and validation.

Latest update

0.2.2 — npm README cleanup: release notes stay inline; removed pointers to repo-only markdown files.

0.2.0 — structured validation (ValidationReason, validatePhoneNumber, onValidationChange), parsePhoneValue, getCountryOptions(), improved PhoneInput a11y (click-outside, listbox ARIA) and RTL tests.

Features

  • 245 supported calling regions derived from libphonenumber-js metadata and world-countries
  • Headless hook via usePhoneInput() plus an optional unstyled reference PhoneInput component (class names only — no bundled CSS)
  • Searchable country selector with country name, ISO code, and dial-code matching
  • Real formatting and validation powered by libphonenumber-js
  • International detection for pasted or typed + / 00 numbers
  • TypeScript-first exports for countries, helpers, hook return values, and component refs

Installation

npm install @input-kit/phone

Quick Start

Component

import { PhoneInput } from '@input-kit/phone';
import '@your-app/phone-input.css'; // style .phone-input-* classes

function Example() {
  return (
    <PhoneInput
      defaultCountry="US"
      onChange={(phone, country) => {
        console.log(phone, country?.code);
      }}
    />
  );
}

Hook

import { usePhoneInput } from '@input-kit/phone';

function Example() {
  const {
    inputProps,
    country,
    countryButtonProps,
    filteredCountries,
    selectCountry,
    isOpen,
    isValid,
  } = usePhoneInput({
    defaultCountry: 'US',
    onChange: (phone, nextCountry) => console.log(phone, nextCountry?.dialCode),
  });

  return (
    <div>
      <button {...countryButtonProps}>
        {country?.flag} {country?.dialCode}
      </button>

      {isOpen && (
        <div>
          {filteredCountries.map((candidate) => (
            <button key={candidate.code} onClick={() => selectCountry(candidate)}>
              {candidate.flag} {candidate.name} {candidate.dialCode}
            </button>
          ))}
        </div>
      )}

      <input {...inputProps} />
      {!isValid && <span>Invalid phone number</span>}
    </div>
  );
}

Phone values

| Field | Meaning | | --- | --- | | phone | National digits stored by the hook (default) | | fullPhone | National number plus dial code when includeDialCode is true | | onChange(phone, country) | Same contract as phone / includeDialCode | | E.164 for APIs | parsePhoneValue(phone, country).e164 when valid — prefer this over raw concatenation |

Known behavior

Formatting, length checks, and validity follow libphonenumber-js (same family as react-phone-number-input). The package does not implement per-country rules outside that library.

Form integrations

Controlled value / onChange with usePhoneInput or PhoneInput. React Hook Form: wrap with Controller and pass field.value, field.onChange, and field.onBlur. Use onValidationChange to sync isValid / message with form errors. Submit-time checks: parsePhoneValue(phone, country) or validatePhoneNumber.

Styled example

A minimal Vite demo using only the hook lives in examples/react-styled/.

Migration from 0.1.x

Compatibility aliases remain exported. Prefer the newer names in new code:

| Deprecated | Replacement | | --- | --- | | setValue | setPhone | | value (hook) | phone | | allowedCountries | onlyCountries | | excludedCountries | excludeCountries | | autoDetectCountry | autoDetect | | toggle / open / close | toggleDropdown / openDropdown / closeDropdown | | countries (hook list) | filteredCountries | | countrySelectorProps | countryButtonProps |

Validation is unified in 0.2.0: isValid and error come from the same validatePhoneNumber call. Use validationReason or onValidationChange for structured form messages.

Development

Requires Bun (see packageManager in package.json).

bun install
bun run test
bun run typecheck
bun run build
bun run lint

Manual browser check: test-demo/ (static HTML).

Exports

Components and hooks

  • PhoneInput
  • usePhoneInput(options)

Country data

  • countries
  • getCountryByCode(code)
  • getCountryByDialCode(dialCode)
  • getCountriesByDialCode(dialCode)
  • getCountryOptions({ locale?, preferredCountries?, excludeCountries?, onlyCountries? })
  • detectCountryFromPhone(phone)

Utilities

  • cleanPhone, formatPhone, unformatPhone, validatePhone, validatePhoneLength
  • validatePhoneNumber{ isValid, reason, message, error }
  • parsePhoneValue{ country, nationalNumber, e164, isValid }
  • addDialCode, removeDialCode, filterCountries, getPlaceholder

Compatibility aliases: stripNonDigits, detectCountry, formatPhoneNumber, parseToE164, getNationalNumber, isPhoneNumberComplete, formatAsYouType, normalizePhoneNumber, phoneNumbersEqual, getCountryDisplayLabel, limitInputLength.

usePhoneInput(options)

| Option | Type | Default | Description | | --- | --- | --- | --- | | defaultCountry | string | 'US' | Default selected country | | preferredCountries | string[] | - | Countries shown first in search results | | excludeCountries | string[] | - | Countries to exclude | | onlyCountries | string[] | - | Restrict selection to these countries | | autoDetect | boolean | true | Detect country from international numbers | | formatOnType | boolean | true | Apply live formatting | | includeDialCode | boolean | false | Return values with dial code included | | required | boolean | false | Empty value is invalid | | validator | (phone, country) => boolean | - | Custom validation override | | onValidationChange | (state) => void | - | Fires when validation result changes |

Important returned fields: phone, fullPhone, country, isValid, validationReason, error, onValidationChange, filteredCountries, inputProps, countryButtonProps, dropdownProps, getCountryOptionId.

License

MIT © Input Kit