# InputPhone

A cross-platform phone-number input with an integrated, searchable country selector (flags and dial codes); ships with a `usePhoneNumber` hook for formatting and country detection.
<!-- BEGIN:xui-mcp-instructions:input-phone -->
A specialised input for entering a phone number. Combines a Select (country/region picker) with a text field for the number itself. Used whenever the product requires a valid, internationally formatted phone number from the user.

### When to use

- Registration and account setup forms requiring a phone number
- Two-factor authentication (2FA) enrollment flows
- Payment and billing forms where a contact number is required
- Any context where the phone number must include a country code

### When not to use

When a simple freeform text field is sufficient and international formatting is not required — use a plain Input instead

When the phone number does not need country selection (e.g. a single-country product with a fixed dial code)

Do not use for non-phone numeric fields (e.g. zip codes, card numbers) — use Input with appropriatetype

### Content guidelines

Placeholder mask should show the expected format for the selected country (e.g. (000) 000-0000 for US, 00 0000 0000 for AU). Update it dynamically when the country changes.

Dial code (e.g. +1) should be displayed as a non-editable prefix, not typed by the user.

Error messages should be specific: *"Enter a valid phone number"*, not just *"Invalid input"*. If the format is wrong, indicate the expected format.

Country selector label — use the flag icon as the primary identifier; always include the country name in the dropdown list for accessibility.

### Behaviour guidelines

Auto-format the number as the user types, applying the mask for the selected country (e.g. spaces, dashes, parentheses). This reduces input errors and improves readability.

Auto-detect country from the number if the user pastes a full international number with a + prefix — update the flag and dial code accordingly.

Strip non-numeric characters on submit; store the number in E.164 format (e.g. +14155551234) regardless of display format.

Preserve the country selection when the user clears the number field with the Remove button — only the number should be cleared, not the country.

Validate on blur, not on every keystroke, to avoid showing errors before the user has finished typing.

Country list should be searchable when it contains more than ~20 entries. Prioritise common countries at the top (e.g. based on locale or previous selection).

### Accessibility

The country selector must have an accessible name (e.g. aria-label=*"Select country"*) — flag icons alone are not sufficient for screen readers.

The dial code prefix must be announced as part of the field context, either via aria-label on the input or by including it in a visible label.

The phone input should use type=*"tel"* to trigger the numeric keyboard on mobile.

The input should have autocomplete=*"tel"* to support autofill.

Error messages must be linked via aria-describedby so screen readers announce them on focus.

Ensure the country selector and number field are reachable and operable via keyboard alone.
<!-- END:xui-mcp-instructions:input-phone -->

## Installation

```bash
npm install @xsolla/xui-input-phone
```

## Imports

```tsx
import {
  InputPhone,
  usePhoneNumber,
  allCountries,
  defaultCountries,
  getCountryByCode,
  getCountryByDialCode,
  getFlag,
} from '@xsolla/xui-input-phone';
import type {
  InputPhoneProps,
  Country,
  UsePhoneNumberOptions,
  UsePhoneNumberReturn,
} from '@xsolla/xui-input-phone';
```

## Quick start

```tsx
const [phone, setPhone] = useState('');

<InputPhone
  value={phone}
  onChangeText={setPhone}
  placeholder="+1 (000) 000-0000"
/>;
```

## API Reference

### `<InputPhone>`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | — | Controlled value. |
| `placeholder` | `string` | — | Placeholder shown when empty. |
| `onChange` | `(e: ChangeEvent<HTMLInputElement>) => void` | — | Native change handler. |
| `onChangeText` | `(text: string) => void` | — | Simplified change handler. |
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Control size. |
| `name` | `string` | — | Form field name. |
| `disabled` | `boolean` | `false` | Disable the control. |
| `error` | `boolean` | `false` | Apply error styling. |
| `errorMessage` | `string` | — | Error message; also marks the control invalid. |
| `countries` | `Country[]` | `allCountries` | Countries available in the dropdown. |
| `selectedCountry` | `Country` | — | Currently selected country. |
| `onCountryChange` | `(country: Country) => void` | — | Fired when the country changes. |
| `extraClear` | `boolean` | `false` | Show a clear button when the input has a value. |
| `onRemove` | `() => void` | — | Fired when the clear button is pressed. |
| `checked` | `boolean` | `false` | Show a success indicator (suppressed when `errorMessage` is set). |
| `checkedIcon` | `ReactNode` | `<CheckCr />` | Override the checked icon. |
| `id` | `string` | `auto` | HTML id. |
| `aria-label` | `string` | — | Accessible label when no visible label is present. |
| `testID` | `string` | — | Test identifier. |

`InputPhone` also accepts the standard `InputHTMLAttributes` (excluding `size` and `onChange`).

Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).

### Hooks

#### `usePhoneNumber(options?)`

Manages a phone-number value with normalisation, formatting (via `libphonenumber-js`) and automatic country detection.

| Option | Type | Description |
| --- | --- | --- |
| `initialCountry` | `string` | ISO 3166-1 alpha-2 code (e.g. `'US'`). |
| `initialValue` | `string` | Initial phone-number value. |
| `onChange` | `(phoneNumber: string) => void` | Fired when the phone number changes. |
| `onCountryChange` | `(country: Country \| undefined) => void` | Fired when the country changes. |

Returns:

| Field | Type | Description |
| --- | --- | --- |
| `phoneNumber` | `string` | Raw phone number (with country code). |
| `formattedPhoneNumber` | `string` | Display-formatted phone number. |
| `country` | `Country \| undefined` | Detected/selected country. |
| `countries` | `Country[]` | All available countries (`allCountries`). |
| `handlePhoneChange` | `(value: string) => void` | Handle phone input change. |
| `handleCountryChange` | `(country: Country) => void` | Handle country selection. |
| `setPhoneNumber` | `(value: string) => void` | Set the phone number programmatically. |

### `allCountries`

`Country[]` — full list of supported countries.

### `defaultCountries`

`Country[]` — curated default subset.

### `getCountryByCode`

`(code: string) => Country | undefined` — look up by ISO alpha-2 code.

### `getCountryByDialCode`

`(dialCode: string) => Country | undefined` — look up by dial code (e.g. `'+44'`).

### `getFlag`

`(code: string) => ReactNode` — get the flag icon for a country code.

### Types

```ts
interface Country {
  code: string;       // ISO 3166-1 alpha-2 (e.g. "US")
  name: string;
  dialCode: string;   // e.g. "+1"
  flag?: ReactNode;
}
```

## Examples

### With country selection

```tsx
const [phone, setPhone] = useState('');
const [country, setCountry] = useState<Country | undefined>();

<InputPhone
  value={phone}
  onChangeText={setPhone}
  selectedCountry={country}
  onCountryChange={setCountry}
/>;
```

### Using `usePhoneNumber`

Reach for the hook when you want normalisation, formatting (via `libphonenumber-js`) and automatic country detection bundled together; reach for the imperative `selectedCountry`/`onCountryChange` API when you already manage the value and only need a controlled country selector.

```tsx
const { phoneNumber, country, handlePhoneChange, handleCountryChange } =
  usePhoneNumber({ initialCountry: 'MY' });

<InputPhone
  value={phoneNumber}
  onChangeText={handlePhoneChange}
  selectedCountry={country}
  onCountryChange={handleCountryChange}
/>;
```

### Custom countries

```tsx
const countries: Country[] = [
  { code: 'US', name: 'United States', dialCode: '+1' },
  { code: 'GB', name: 'United Kingdom', dialCode: '+44' },
  { code: 'DE', name: 'Germany', dialCode: '+49' },
];

<InputPhone countries={countries} />;
```

### Sizes

```tsx
<InputPhone size="xs" placeholder="Extra small" />
<InputPhone size="sm" placeholder="Small" />
<InputPhone size="md" placeholder="Medium" />
<InputPhone size="lg" placeholder="Large" />
<InputPhone size="xl" placeholder="Extra large" />
```

### Error state

```tsx
<InputPhone
  value="123"
  error
  errorMessage="Please enter a valid phone number"
/>
```

## Accessibility

- The country selector uses `role="combobox"`; the list uses `role="listbox"` with `role="option"` items, linked via `aria-controls`.
- The phone field uses `type="tel"` and `inputMode="tel"`.
- Keyboard: ArrowDown opens / advances; ArrowUp moves up; Enter selects; Escape closes the dropdown.
- `errorMessage` is linked via `aria-describedby` and marks the input invalid.
