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

@intl-ui/react

v1.0.5

Published

React components and hooks for international UI: phone input, country selector, city selector. Headless-first, accessible, framework-free styling.

Readme

@intl-ui/react

npm bundle types license

React components and hooks for international phone input. Headless-first, accessible, zero-config styling.

Part of the @intl-ui ecosystem.

Live Demo  ·  Angular version  ·  GitHub


Install

npm install @intl-ui/react
# or
pnpm add @intl-ui/react
# or
yarn add @intl-ui/react

@intl-ui/core is bundled automatically — no separate install needed.

Peer dependency: react ^18.2.0 || ^19.0.0


Quick start

import { PhoneInput } from '@intl-ui/react';

function App() {
  return (
    <PhoneInput
      defaultCountry="co"
      onValueChange={(value, meta) => {
        console.log(value);         // "+573105551234"
        console.log(meta.isValid);  // true
        console.log(meta.country);  // { name: "Colombia", iso2: "co", ... }
      }}
    />
  );
}

That's it. You get a fully working phone input with country dropdown, auto-detection, keyboard navigation, and validation. See it running in the live demo.


Three levels of abstraction

Pick the level that fits your use case. All three share the same core logic — you can always drop down a level for more control.

1. <PhoneInput /> — one-liner

Works out of the box with sensible defaults.

import { PhoneInput } from '@intl-ui/react';

<PhoneInput defaultCountry="us" onValueChange={(v) => console.log(v)} />

2. Compound components — full layout control

Every child reads from a shared context. No prop drilling.

import { PhoneInput } from '@intl-ui/react';

<PhoneInput.Root defaultCountry="co" onValueChange={(v) => console.log(v)}>
  <PhoneInput.CountrySelect>
    <PhoneInput.Flag />
    <PhoneInput.DialCode />
  </PhoneInput.CountrySelect>
  <PhoneInput.Input placeholder="Phone number" />
  <PhoneInput.CountryList>
    {(country, index) => (
      <PhoneInput.CountryListItem
        key={country.iso2}
        country={country}
        index={index}
      />
    )}
  </PhoneInput.CountryList>
</PhoneInput.Root>

Every compound child supports className, style, event handlers, and the asChild prop for rendering your own element:

<PhoneInput.Input asChild>
  <MyDesignSystemInput placeholder="Phone" />
</PhoneInput.Input>

3. usePhoneInput() — headless hook

Maximum control. The hook returns state, prop-getters, and actions — you render whatever you want.

import { usePhoneInput } from '@intl-ui/react';

function CustomPhoneInput() {
  const {
    country, isValid, visibleCountries,
    getInputProps, getCountrySelectProps,
    getCountryListProps, getCountryOptionProps,
  } = usePhoneInput({ defaultCountry: 'co' });

  return (
    <div>
      <button {...getCountrySelectProps()}>
        {country?.flag} +{country?.dialCode}
      </button>
      <input {...getInputProps()} />
    </div>
  );
}

Features

  • 200 countries with dial codes, format masks, capitals, flags, and regions
  • Auto country detection — type +380 and Ukraine is detected via the dial-code trie
  • ISO code shortcut — type co, usa, gb to switch country instantly
  • Auto-prefix + — click the input and the + appears, ready for dial code digits
  • Backspace-aware — clear the input and type a new prefix without fighting the formatter
  • Searchable dropdown — filter by name, ISO code, or dial code
  • Keyboard navigation — arrows, Enter, Escape, typeahead
  • asChild pattern — clone your own elements with Radix-style Slot
  • Controlled + uncontrolledvalue / defaultValue + onValueChange, same as native <input>
  • Zero styling opinions — the package ships no CSS, use whatever you want
  • 4.8 KB gzipped (hook + 7 compound components + Slot + Context)

Three ways to set the country

| Method | Example | |---|---| | Dropdown search | Click trigger → type colomb → pick Colombia | | Type +code | Type +380 → Ukraine detected automatically | | ISO shortcut | Type co, usa, gb, deu → instant switch |


Compound components

| Component | Element | Description | |---|---|---| | <PhoneInput /> | <div> | One-liner wrapper with sane defaults | | <PhoneInput.Root> | — | Provider. Calls the hook, publishes via context | | <PhoneInput.Input> | <input> | The phone number field (type="tel") | | <PhoneInput.CountrySelect> | <button> | Trigger that opens the dropdown | | <PhoneInput.Flag> | <span> | Emoji flag of the selected country | | <PhoneInput.DialCode> | <span> | +57 label | | <PhoneInput.CountryList> | <ul> | Dropdown listbox (render-prop children) | | <PhoneInput.CountryListItem> | <li> | One country option |


Hook API — usePhoneInput(options)

Options

| Option | Type | Default | Description | |---|---|---|---| | value | string | — | Controlled phone value (E.164) | | defaultValue | string | "" | Initial value for uncontrolled mode | | onValueChange | (value, meta) => void | — | Fired on every change with parsed metadata | | country | CountryIso2 | — | Controlled country ISO2 | | defaultCountry | CountryIso2 | — | Initial country for uncontrolled mode | | onCountryChange | (country) => void | — | Fired when the country changes | | disableCountryGuess | boolean | false | Don't auto-detect country from typed input | | countries | Country[] | all 200 | Custom country list | | preferredCountries | CountryIso2[] | [] | Countries pinned to top of the dropdown |

Return value

| Field | Type | Description | |---|---|---| | value | string | Canonical E.164 string | | inputValue | string | Formatted string displayed in the input | | country | Country \| null | Selected country object | | parsed | ParsedPhone \| null | Full parsed phone (e164, national, international, isValid, ...) | | isValid | boolean | Whether the number is valid | | isOpen | boolean | Dropdown open state | | focusedIndex | number | Focused country index (-1 if none) | | filter | string | Current dropdown filter text | | visibleCountries | Country[] | Filtered country list | | getInputProps | () => Props | Spread onto <input> | | getCountrySelectProps | () => Props | Spread onto trigger <button> | | getCountryListProps | () => Props | Spread onto <ul> | | getCountryOptionProps | (country, index) => Props | Spread onto each <li> | | setCountry | (iso2) => void | Set country programmatically | | setOpen | (open) => void | Open/close dropdown | | setFilter | (text) => void | Filter the country list | | reset | () => void | Reset to initial state |


Advanced: custom compound children

Use usePhoneInputContext() to build your own compound children that read from <PhoneInput.Root>:

import { PhoneInput, usePhoneInputContext } from '@intl-ui/react';

function ValidationBadge() {
  const { isValid, country } = usePhoneInputContext();
  return isValid
    ? <span>&check; Valid {country?.name} number</span>
    : <span>Enter a valid number</span>;
}

<PhoneInput.Root defaultCountry="co">
  <PhoneInput.Input />
  <ValidationBadge />
</PhoneInput.Root>

Bundle size

| Package | Gzipped | |---|---| | @intl-ui/react | 4.8 KB | | @intl-ui/core (bundled) | 9.4 KB | | Total | ~14 KB |


Ecosystem

  • @intl-ui/core — Framework-agnostic phone parsing, formatting, validation, and country data. Works in Node, Deno, Bun, and the browser without React.
  • @intl-ui/angular — Angular 17+ components, directives, and validators with signal-based reactivity and Reactive Forms support.

License

MIT © John Alberto López Hernández