Phone Input
An international phone input with country select and formatting.
""
Try pasting +44 7700 900123 โ the country follows the number.
Installation
Usage
A searchable country picker joined to a number field, storing one E.164 string. Country lookup and digit grouping ship in the component, so nothing here pulls in a metadata library.
import { PhoneInput } from "@/components/ui/phone-input";
export function ContactField() {
const [phone, setPhone] = React.useState("");
return <PhoneInput value={phone} onValueChange={setPhone} defaultCountry="PT" />;
}The value is "+351912345678" โ country code included, no spaces, no punctuation. That is what
tel: links, SMS providers, and every phone column want, and it is the one format that survives a
user changing country later.
Pasting a full number
Paste +44 7700 900123 into the field and the country switches to the United Kingdom on its own,
because the dial code is in the string. Longest-prefix matching means +1 picks the US while +351
picks Portugal rather than a shorter partial match.
Setting value from outside behaves the same way, so a number loaded from your API renders with the
right flag without you resolving it first.
Countries
countries replaces the built-in list โ restrict it to where you actually operate, reorder it to
put your main market first, or extend it:
import { PhoneInput, DEFAULT_COUNTRIES } from "@/components/ui/phone-input";
<PhoneInput countries={DEFAULT_COUNTRIES.filter((country) => EU.includes(country.code))} />;Each entry is { code, name, dial, groups? }. groups is the digit grouping used for display โ
[3, 3, 3] renders 912 345 678. It only affects presentation; the stored value never contains
spaces.
Flags are emoji derived from the ISO code, so there are no image assets to host and nothing to localize.
Scope
Formatting here is grouping, not validation. Real phone-number rules โ valid prefixes per carrier,
national trunk digits, variable lengths โ need a metadata library such as libphonenumber-js, which
is roughly 145 kB. If you need to verify a number, validate on the server; if you need it to look
right as it is typed, this is enough.
Props
| Prop | Description |
|---|---|
value / defaultValue / onValueChange | Controlled or uncontrolled E.164 string. |
defaultCountry | ISO code selected before the user picks one. Defaults to "US". |
onCountryChange | Called with the country whenever it changes. |
countries | Replace the built-in list. |
name | Emits a hidden input with the E.164 value for plain HTML forms. |
aria-label | Labels the number field. Defaults to "Phone number". |
Accessibility
The country trigger announces the current country rather than only showing a flag, since flag emoji
read as the country name in some screen readers and as nothing in others. The number field is a
real type="tel" input with autocomplete="tel", so browser autofill works.