PhoneCountryPicker combines a country flag trigger, popup, search and calling-code choices. Use the complete component for ordinary selection, or compose Root, Trigger and Options with Dropdown when you need a custom popup. Both forms share the same prepared countries and selection.
Usage
Supply your own country data and a trigger accessible name. Usevalue with onValueChange for controlled selection, or defaultValue for uncontrolled selection. null represents no selected country.
PhoneCountryOption has a unique stable value, a localized country label, a digits-only callingCode without the leading plus sign, and a decorative flag node. It may also be disabled. Values are caller-owned identifiers, such as ISO country codes, and are returned unchanged. Labels, flags and calling codes are presentation data and do not become the selected identifier.
The picker adds the plus sign and isolates calling codes left to right in option rows and overflow tooltips, including in right-to-left interfaces. Supply countries in the order you want them displayed. The selected country appears first while it matches the query; the remaining matches retain their supplied order.
Compound composition
This is a breaking API change for existing compound callers. AddPhoneCountryPicker.Root around Dropdown.Root, move countries, value and onValueChange from Options to Root, and remove Trigger.country. Both parts now require Root and derive the selected country from it.
Root owns countries and the controlled or uncontrolled selection. Put the shared Dropdown.Root inside it and compose the trigger, content and options. Trigger derives its selected flag and accessible description from Root; Options reads the same data and selection. Do not supply another country or selected value to those parts.
dropdownProps and contentProps on the complete picker, or configure the Dropdown parts directly in compound usage.
Search and selection
Search matches substrings of the supplied country labels, stable identifiers and calling codes. Labels and identifiers use the sharednormalizeSearchText normalization: lowercase text, Unicode NFD decomposition and script-aware accent handling. For example, reunion matches Réunion and gb matches a GB identifier. Meaningful distinctions in scripts are preserved according to that helper. Surrounding query whitespace is trimmed; internal whitespace is preserved. For calling-code search, one leading plus sign is optional, so 44, +44 and " +44 " match the supplied calling code 44. Search does not parse a full phone number or infer a country from it.
onValueChange(value, eventDetails) receives the requested identifier and the shared Dropdown.SelectionGroup details: reason, the native event, the selected option trigger, cancel() and isCanceled. Call cancel() before updating controlled state to reject the request. A canceled request preserves uncontrolled selection and leaves the popup open. Set closeOnSelect={false} to keep it open after an accepted selection. Selection changes do not modify any phone input value.
Set disabled on the complete picker or Root to disable its trigger, search and selection. A disabled country stays visible and cannot be selected. Trigger.disabled and Options.disabled can additionally disable those individual parts.
Native targets and labels
The complete picker’s native attributes, event handlers,className, style, ref, render and optional children target its trigger. children replaces the default flag or world icon and chevron. contentProps targets Dropdown.Content; optionsProps targets the options root div and supplies searchLabel, listLabel and emptyLabel. In compound usage, native props, refs and render composition target the corresponding Trigger button or Options div. Root renders no DOM node.
Supply localized country labels and the required trigger aria-label. The selected country label describes the trigger, while its flag and chevron are decorative. An empty selection shows a world icon. The popup takes the trigger name unless a Dropdown.Title or an explicit content label names it. searchLabel defaults to Search and names both the search field and its placeholder. listLabel defaults to Countries and names the listbox independently. emptyLabel defaults to No results.
Phone input integration
The picker ships without a country catalog, flag asset package or phone-input runtime. The frontend adapter owns phone parsing, validation, formatting, editing drafts and persistence. Map the phone library’s selected country toRoot.value or the complete picker’s value, and map country changes back through one callback. Keep the phone text in its existing owner when changing or clearing a country, or applying an external selected-country update.
Native form submission, reset, required, read-only and Field integration for this custom picker are deferred. The complete picker forwards native trigger attributes according to the Dropdown button contract.
Front component renderer
The React and Preact gallery fixtures include the complete picker and compound composition. Portal placement, geometry, scrolling, dismissal and focus belong to the shared Dropdown and renderer integration. The picker does not introduce a separate renderer runtime.Props
string
required
Required accessible name for the country selector trigger.
ReactNode
Optional trigger content replacing the default country flag or world icon and chevron.
string | ((state: PopoverTriggerState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
number
default:"0"
How long to wait before closing the popover that was opened on hover.
Specified in milliseconds.Requires the
openOnHover prop.boolean
Closes the dropdown after an accepted selection. Defaults to true.
Omit<DropdownContentProps, "children">
Props for the internally assembled Dropdown.Content, including its accessible name, placement, width and popup ref.
readonly PhoneCountryOption[]
required
Prepared choices with a unique stable value, localized label, digits-only calling code without a plus sign, decorative flag node and optional disabled state. Matching choices retain their supplied order after the selected country.
string | null
Initial uncontrolled country identifier. Omit it or use null to start empty.
number
default:"300"
How long to wait before the popover may be opened on hover. Specified in milliseconds.Requires the
openOnHover prop.boolean
Disables the trigger, search and country selection.
Omit<DropdownRootProps, "children" | "type" | "multiple">
Props for the internally assembled Dropdown.Root, including controlled open state and open-change events. The picker fixes its type to picker.
string
ID of the trigger. In addition to being forwarded to the rendered element,
it is also used to specify the active trigger for the popover in controlled mode (with the PopoverRoot
triggerId prop).boolean
default:"true"
Whether the component renders a native
<button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (e.g. <div>).((value: string | null, eventDetails: DropdownSelectionGroupChangeEventDetails) => void)
Receives the requested country identifier or null and Dropdown.SelectionGroup event details, including reason, event, trigger and cancel(). Cancel before updating controlled state to reject a selection.
boolean
default:"false"
Whether the popover should also open when the trigger is hovered.
PhoneCountryPickerOptionsProps
Props for the options root div, including native attributes, ref, render, searchLabel, listLabel and emptyLabel. Search matches normalized labels and identifiers, or calling codes with an optional leading plus sign. Surrounding query whitespace is trimmed.
Ref<HTMLButtonElement>
Ref to the trigger button, or the element composed through render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTriggerState>
Composes the trigger with an element or render callback. Native attributes, handlers, className, style and ref target the trigger.
CSSProperties | ((state: PopoverTriggerState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
string | null
Controlled country identifier. Use null to clear the selection. Labels and calling codes do not replace this value.
PhoneCountryPicker.Root
ReactNode
required
A Dropdown.Root with type=“picker”, its Trigger, Content and Options. Root shares prepared data and selection and renders no DOM node.
boolean
default:"true"
Closes the composed Dropdown after an accepted selection. Defaults to true.
readonly PhoneCountryOption[]
required
Prepared choices shared by Trigger and Options, with unique stable values, localized labels, digits-only calling codes without a plus sign, decorative flag nodes and optional disabled states.
string | null
default:"null"
Initial uncontrolled country identifier. Omit it or use null to start empty.
boolean
default:"false"
Disables the composed trigger, search and country selection.
((value: string | null, eventDetails: DropdownSelectionGroupChangeEventDetails) => void)
Receives the requested identifier or null and Dropdown.SelectionGroup event details, including reason, event, trigger and cancel(). Cancel before updating controlled state to reject a selection.
string | null
Controlled country identifier shared by the parts. Use null to clear the selection.
PhoneCountryPicker.Trigger
string
required
Required accessible name for the country selector button.
ReactNode
Optional trigger content replacing the selected country flag or empty world icon and chevron. The selected label comes from Root.
string | ((state: PopoverTriggerState) => string | undefined)
CSS class applied to the element, or a function that
returns a class based on the component’s state.
number
default:"0"
How long to wait before closing the popover that was opened on hover.
Specified in milliseconds.Requires the
openOnHover prop.number
default:"300"
How long to wait before the popover may be opened on hover. Specified in milliseconds.Requires the
openOnHover prop.boolean
Disables the native trigger button in addition to Root.disabled.
string
ID of the trigger. In addition to being forwarded to the rendered element,
it is also used to specify the active trigger for the popover in controlled mode (with the PopoverRoot
triggerId prop).boolean
default:"true"
Whether the component renders a native
<button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (e.g. <div>).boolean
default:"false"
Whether the popover should also open when the trigger is hovered.
Ref<HTMLButtonElement>
Ref to the trigger button, or the element composed through render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTriggerState>
Composes the Dropdown trigger with an element or render callback. Native attributes, handlers, className, style and ref target this element.
CSSProperties | ((state: PopoverTriggerState) => CSSProperties | undefined)
Style applied to the element, or a function that
returns a style object based on the component’s state.
PhoneCountryPicker.Options
boolean
Disables search and selection in addition to Root.disabled.
string
default:"No results"
Status message shown when no country label, identifier or calling code matches. Defaults to No results.
string
default:"Countries"
Accessible name of the country listbox, separate from the search field. Defaults to Countries.
Ref<HTMLDivElement>
Ref to the options root div, or the element composed through render.
ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>
Composes the non-interactive options root with an element or render callback. Native attributes, handlers, className, style and ref target this element. searchLabel names the internal search input.
string
default:"Search"
Accessible name and placeholder of the country search field. Defaults to Search. Search matches normalized labels and identifiers, or calling codes with an optional leading plus sign. Surrounding query whitespace is trimmed.