@xsolla/xui-input-phone
v0.175.0
Published
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 en
Downloads
11,930
Readme
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.
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.
Installation
npm install @xsolla/xui-input-phoneImports
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
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
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
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.
const { phoneNumber, country, handlePhoneChange, handleCountryChange } =
usePhoneNumber({ initialCountry: 'MY' });
<InputPhone
value={phoneNumber}
onChangeText={handlePhoneChange}
selectedCountry={country}
onCountryChange={handleCountryChange}
/>;Custom countries
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
<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
<InputPhone
value="123"
error
errorMessage="Please enter a valid phone number"
/>Accessibility
- The country selector uses
role="combobox"; the list usesrole="listbox"withrole="option"items, linked viaaria-controls. - The phone field uses
type="tel"andinputMode="tel". - Keyboard: ArrowDown opens / advances; ArrowUp moves up; Enter selects; Escape closes the dropdown.
errorMessageis linked viaaria-describedbyand marks the input invalid.
