PhoneInput

Country-aware phone number input that emits E.164 values.

Quick Preview

Usage

<PhoneInput value={phone} onChange={setPhone} defaultCountry="NG" />

onChange always receives E.164 — "+2348012345678" — or undefined when nothing dialable has been typed yet.

Examples

Default PhoneInput

Use this as the starting point for PhoneInput. Keep product data, routing, fetching, and authorization logic in the consuming app.

National and international entry

The same number can be written either way round, and both are accepted. 0801 234 5678 is read against the selected country, national trunk prefix and all. A leading + — typed or pasted — switches the field to international entry: the number keeps its own calling code, the country selector follows the digits as soon as they name one country, and the selector stops repeating the code the field is already showing.

Type 0801 234 5678 or paste +234 801 234 5678. A leading + switches to international entry and moves the country selector to match.

Emitted value: undefined

Picking a country from the selector rewrites the code in front of the digits rather than discarding them, so switching from Nigeria to Ghana turns +234 801 234 5678 into +233 801 234 5678.

Numbers group as they are typed whether or not the trunk prefix is there: 8161827754 reads as 816 182 7754 and 08161827754 as 0816 182 7754. Most countries only publish a national format that assumes the prefix, so the second grouping is borrowed from the international pattern with the calling code taken back off — the digits shown are always the digits typed.

Trailing addon

rightAddon pins content to the trailing edge of the field — a validity tick, a "Verify" button, an extension field. It sits outside the number, so it never shifts as the number is formatted.

Props

Prop/APITypeDefaultDescription
valuestring-Controlled E.164 phone value.
onChange(value: string | undefined) => void-Called with the next E.164 value, or undefined when the field holds nothing dialable.
defaultCountryCountry code"NG"Country the field starts on, before anything is typed.
rightAddonReactNode-Content pinned to the trailing edge of the field.
size"sm" | "md" | "lg" | { base, md?, lg? }"md"Field scale. Inherits from SizeProvider.
disabledbooleanfalseDisables the field and the country selector.
readOnlybooleanfalseMakes the number uneditable.
placeholderstring"(0000) 000-0000"Placeholder for the number, not the calling code.
id, namestring-Forwarded to the underlying input.
aria-invalidboolean-Marks the field invalid.
classNamestring-Local layout or spacing overrides.

Validate the emitted value with validatePhoneNumber from @giddaa-housing/ui/utils/phone rather than a regex — it is the same data set the field formats with.

Accessibility

  • Provide visible labels or accessible names for interactive controls.
  • The country selector returns focus to the number field on every close — selection, Escape, or an outside click — so the tab order does not jump.
  • Anything interactive in rightAddon needs its own accessible name; it is outside the field's label.
  • Preserve the component-provided focus, keyboard, disabled, and invalid-state behavior.
  • Do not communicate state with color alone; include text, icons, or helper copy.

On this page