Country-aware phone builder with flag selector, dial codes, format validation, and E.164 storage.
- For a basic phone text input without country metadata, use
field.tel()instead - The renderer shows a country picker with flags, dial codes, and optional search
storeE164()normalizes the output to+33612345678format for API consumption
Defaults, inheritance & field methods
- defaultValue is
null - type is
phone - placeholder defaults to
'Enter phone number' - debounce defaults to
0for immediate formatting feedback - default country is
'FR', preferred countries default to['FR', 'US', 'GB', 'DE', 'ES'] - Shared methods: see Base field builder
Phone-specific methods:
| Method | Type | Description |
|---|---|---|
defaultCountry(code) | code: string | Sets the initial selected country. |
preferredCountries(codes) | codes: string[] | Shortlists countries shown at the top of the picker. |
searchable(value = true) | value?: boolean | Enables country search inside the picker. |
showFlag(value = true) | value?: boolean | Shows or hides the country flag. |
showDialCode(value = true) | value?: boolean | Shows or hides the dial code prefix. |
countryLayout(layout) | 'integrated' | 'detached' | Chooses whether the country selector sits inside the same bordered shell as the input (integrated, default) or as a separate button before it (detached). Drives both the default styling and a data-fb-layout attribute you can target in CSS. |
storeE164() | () => this | Stores a normalized E.164 string (e.g. +33612345678) instead of the richer { country, national, e164 } phone payload. Use this when the backend expects a flat string. |
validateFormat(value = true) | value?: boolean | Enables libphonenumber-based format validation. Adds three sequential checks: isPossible(), isValid(), and a format parse - each with its own error message. Disable with validateFormat(false) if you need lenient input. |
Per-render props (text, render, passthroughs)
Beyond the builder methods above, every <fields.phone /> renderer accepts props to customize copy, swap rendering, or forward attributes to the underlying inputs. Pass them directly on the rendered field:
| 1 | <fields.phone |
| 2 | searchPlaceholderText="Search a country…" |
| 3 | emptySearchText={({ search }) => `No match for "${search}"`} |
| 4 | renderCountryItemContent={({ country, defaultContent, selected }) => ( |
| 5 | <> |
| 6 | {defaultContent} |
| 7 | {selected ? <span>✓</span> : null} |
| 8 | </> |
| 9 | )} |
| 10 | inputProps={{ 'aria-describedby': 'phone-help' }} |
| 11 | /> |
Text overrides — each accepts a plain string or a function that receives the render context (current country, open state, search query, …).
| Method | Type | Description |
|---|---|---|
countryButtonAriaLabel | string | (ctx) => string | Accessibility label for the country-picker trigger button. Receives the current render context (current country, open state, search, …). |
searchPlaceholderText | string | (ctx) => string | Placeholder for the country-search input inside the picker. Defaults to 'Search country…'. |
emptySearchText | string | (ctx) => string | Text shown when the search query matches no countries. Defaults to 'No countries match "<query>".' / 'No countries available.'. |
e164Text | string | (ctx & { e164 }) => string | Opt-in label rendered alongside the E.164 preview. Only shown when e164Text or renderE164 is provided. |
Render overrides - each receives a defaultContent node so you can wrap the built-in UI or replace it entirely.
| Method | Type | Description |
|---|---|---|
renderCountryButtonContent | (ctx & { defaultContent }) => ReactNode | Full renderer for the country-picker trigger content (flag, dial code, chevron). defaultContent contains the built-in layout so you can wrap it. |
renderCountryItemContent | (ctx & { country, defaultContent, index, selected }) => ReactNode | Full renderer for a single row in the country list. Useful to add a "selected" checkmark or a custom layout. |
renderEmptySearchContent | (ctx & { defaultContent }) => ReactNode | Full renderer for the "no results" state shown inside the picker. |
renderE164 | (ctx & { defaultContent, e164 }) => ReactNode | Opt-in full renderer for the E.164 preview below the input. Only shown when e164Text or renderE164 is provided. |
renderLabel / renderHint / renderError / renderRequiredMark | (ctx) => ReactNode | Inherited from every field - replace the label, hint, error, or required-mark rendering entirely. |
Passthrough props & layout
| Method | Type | Description |
|---|---|---|
inputProps | Web: InputHTMLAttributes · Native: TextInputProps | Passthrough attributes for the phone-number input itself (aria-*, data-*, onKeyDown, testID, …). |
searchInputProps | Web: InputHTMLAttributes · Native: TextInputProps | Passthrough attributes for the country-search input inside the picker. |
wrapperProps / labelProps / hintProps / errorProps | Platform-native props | Passthrough props for the wrapper, label, hint, and error elements. Each merges its style with the default. |
hideLabel | boolean | Hides the visible label while keeping the accessible name via aria-label / accessibilityLabel. |
countryLayout | 'integrated' | 'detached' | Same as the builder method but at the render site. Takes precedence over the schema value for this specific render. |
styles | Record of slot → style | Per-slot style overrides (see the Style keys section below). Each slot is merged over the built-in default, so you can tweak a single property or replace it entirely. |
Style keys
The styles prop accepts an object keyed by renderer slot. Each entry is merged over the built-in default for that slot, so you can tweak one property or replace it.
| 1 | <fields.phone |
| 2 | styles={{ |
| 3 | phoneModalCard: { borderRadius: 20, padding: 16 }, |
| 4 | phoneCountryRow: { paddingVertical: 16 }, |
| 5 | phoneCountryName: { fontWeight: '600' }, |
| 6 | }} |
| 7 | /> |
Native slots (ViewStyle / TextStyle)
| Method | Type | Description |
|---|---|---|
wrapper | ViewStyle | Outer field container. |
label | TextStyle | Field label. |
requiredMark | TextStyle | The asterisk shown next to required labels. |
hint | TextStyle | Helper text below the input. |
error | TextStyle | Error text shown below the input. |
phoneRow | ViewStyle | Row wrapping the country button + phone input. |
phoneCountryButton | ViewStyle | The picker trigger button. |
phoneCountryFlag | TextStyle | Flag emoji inside the trigger and country rows. |
phoneCountryDial | TextStyle | Dial-code text (e.g. +33). |
phoneCountryDivider | ViewStyle | Vertical divider between the country button and input in the integrated layout. |
phoneChevron | TextStyle | Chevron glyph inside the trigger. |
phoneInput | TextStyle | The phone-number TextInput. |
phoneE164 | TextStyle | The opt-in E.164 preview text. |
phoneModalBackdrop | ViewStyle | Dimmed overlay behind the country picker. |
phoneModalCard | ViewStyle | The modal card that contains the country list. |
phoneSearchInput | TextStyle | Country-search input inside the picker. |
phoneCountryRow | ViewStyle | One row in the country list. |
phoneCountryName | TextStyle | Country name inside a row. |
phoneSeparator | ViewStyle | The separator between preferred countries and the rest of the list. |
phoneEmptyText | TextStyle | Empty-state text when no countries match. |
Web slots (CSSProperties)
| Method | Type | Description |
|---|---|---|
wrapper | CSSProperties | Outer field container. |
label | CSSProperties | Field label. |
requiredMark | CSSProperties | The asterisk shown next to required labels. |
hint | CSSProperties | Helper text below the input. |
error | CSSProperties | Error text shown below the input. |
phoneRow | CSSProperties | Row wrapping the country button + phone input. |
phoneCountryButton | CSSProperties | The picker trigger button. |
phoneCountryFlag | CSSProperties | Flag glyph inside the trigger and country rows. |
phoneCountryDial | CSSProperties | Dial-code text (e.g. +33). |
phoneCountryDivider | CSSProperties | Vertical divider between the country button and input in the integrated layout. |
phoneChevron | CSSProperties | Chevron glyph inside the trigger. |
phoneInput | CSSProperties | The phone-number <input>. |
phoneE164 | CSSProperties | The opt-in E.164 preview text. |
phoneSearchWrapper | CSSProperties | Wrapper around the country-search input. |
phoneSearchInput | CSSProperties | Country-search <input>. |
phoneCountryList | CSSProperties | Outer container of the country list popup. |
phoneCountryScroll | CSSProperties | Scrollable container holding the country rows. |
phoneCountryItem | CSSProperties | One row in the country list. |
phoneCountryName | CSSProperties | Country name inside a row. |
phoneSeparator | CSSProperties | The separator between preferred countries and the rest of the list. |
phoneEmptyText | CSSProperties | Empty-state text when no countries match. |
Recipes
Patterns that showcase phone-specific strengths.
International signup
| 1 | const schema = { |
| 2 | phone: field.phone('Phone') |
| 3 | .defaultCountry('FR') |
| 4 | .preferredCountries(['FR', 'US', 'GB']) |
| 5 | .searchable() |
| 6 | .showFlag(true) |
| 7 | .showDialCode(true), |
| 8 | } |
API-ready E.164 output
| 1 | const schema = { |
| 2 | phone: field.phone('Phone') |
| 3 | .storeE164() |
| 4 | .required() |
| 5 | .validateFormat(true), |
| 6 | } |
US-only customer support
| 1 | const schema = { |
| 2 | phone: field.phone('Phone') |
| 3 | .defaultCountry('US') |
| 4 | .preferredCountries(['US']) |
| 5 | .showDialCode(false) |
| 6 | .validateFormat(), |
| 7 | } |
Disabled display field
| 1 | const schema = { |
| 2 | supportLine: field.phone('Support line') |
| 3 | .defaultCountry('US') |
| 4 | .disabled() |
| 5 | .hint('Managed by your account team'), |
| 6 | } |