BetaForms

PhoneNumberField

A national phone number beside its country flag and calling code, with a searchable country sheet. Imports from @xaui/native/phone-number-field. Run the phone-number-field demo screen to try the real component in light and dark mode.

Overview

A national phone number beside its country flag and calling code, with a searchable country sheet. Imports from @xaui/native/phone-number-field. Run the phone-number-field demo screen to try the real component in light and dark mode.

libphonenumber-js is an optional peer of this package, and this component needs it — it supplies the calling codes, the country metadata and the national formatting. It is the only component that imports it, so an app without a phone field never pays for it.

pnpm add libphonenumber-js

First example

import { FieldGroup } from '@xaui/native/field-group'
import { PhoneNumberField } from '@xaui/native/phone-number-field'
;<PhoneNumberField defaultValue={{ country: 'AD', nationalNumber: '' }}>
<PhoneNumberField.Label>Phone number</PhoneNumberField.Label>
<FieldGroup>
<FieldGroup.Prefix>
<PhoneNumberField.Country accessibilityLabel="Choose country" />
</FieldGroup.Prefix>
<PhoneNumberField.Field placeholder="000 000" />
</FieldGroup>
<PhoneNumberField.Description>
We’ll send a verification code.
</PhoneNumberField.Description>
<PhoneNumberField.Overlay />
<PhoneNumberField.Content height="65%" isSwipeable={false}>
<PhoneNumberField.Handle />
<PhoneNumberField.Title>Choose country</PhoneNumberField.Title>
<PhoneNumberField.Search
accessibilityLabel="Search country"
placeholder="Search country…"
/>
<PhoneNumberField.CountryList />
</PhoneNumberField.Content>
</PhoneNumberField>

Anatomy

PhoneNumberField
  Label
  FieldGroup
    FieldGroup.Prefix → Country
    Field
  Description / Error
  Overlay
  Content
    Handle
    Title
    Search
    CountryList
    Close

Usage

import { FieldGroup } from '@xaui/native/field-group'
import { PhoneNumberField } from '@xaui/native/phone-number-field'
;<PhoneNumberField defaultValue={{ country: 'AD', nationalNumber: '' }}>
  <PhoneNumberField.Label>Phone number</PhoneNumberField.Label>
  <FieldGroup>
    <FieldGroup.Prefix>
      <PhoneNumberField.Country accessibilityLabel="Choose country" />
    </FieldGroup.Prefix>
    <PhoneNumberField.Field placeholder="000 000" />
  </FieldGroup>
  <PhoneNumberField.Description>
    We’ll send a verification code.
  </PhoneNumberField.Description>
  <PhoneNumberField.Overlay />
  <PhoneNumberField.Content height="65%" isSwipeable={false}>
    <PhoneNumberField.Handle />
    <PhoneNumberField.Title>Choose country</PhoneNumberField.Title>
    <PhoneNumberField.Search
      accessibilityLabel="Search country"
      placeholder="Search country…"
    />
    <PhoneNumberField.CountryList />
  </PhoneNumberField.Content>
</PhoneNumberField>

Use value and onValueChange for a controlled field. The value is { country: 'FR', nationalNumber: '0612345678' }; an empty number is an empty string. Changing country preserves the national text. Pasting a complete international number selects its country when allowed by countries. Unsupported international pastes leave the current value unchanged. Formatting runs on blur so deletion does not fight inserted spaces. usePhoneNumberField().phoneNumber exposes E.164 for a possible number, otherwise null; possibility is a length check, not proof of assignment or reachability.

countries restricts the list; the selected/default country must belong to it. With no initial value, the first allowed country is selected (US when unrestricted). Names and sorting follow locale. Searching accepts country name, ISO code or calling code. The sheet uses the existing BottomSheet and requires react-native-gesture-handler and a GestureHandlerRootView, plus XAUIProvider's portal host.

Country names need Intl.DisplayNames, which Hermes does not implement. The field does not throw without it — every country falls back to its own ISO code, so the list reads AD, CI, FR and sorts that way, and flags, calling codes and search still work. Install @formatjs/intl-displaynames and import it before your app's entry point to get localized names on React Native.

Root props

This component declares no props of its own beyond its React Native node.

Slots

This component is standalone and exposes no public slot.

Variants, sizes and colour

Variants

The four TextField variants (primary, secondary, tertiary, ghost) and sizes (xs, sm, md, lg) are inherited, including theme colors, focus and invalid states.

Accessibility

The field is associated with its label and description. Give Country an action label in your app's language; the calling code is exposed as its value. Selected country rows announce their name, calling code and selected state. Label the search field and provide an empty-state message. isDisabled prevents both editing and country selection.

Migration from legacy

This is a new component. There is no legacy PhoneNumberField to deprecate.