Region picker
createRegionList is a headless controller for a region selector: the dropdown a user opens to
pick a country calling code. It holds the list of 245 regions, applies your filters, search, and
sort, localizes each name, and emits a new snapshot whenever any of that changes. It renders
nothing; you own the DOM. It requires the engine to be ready and throws EngineNotReadyError
otherwise (see Engine loading).
import { createRegionList } from '@telixon/web-sdk';
const regionList = createRegionList();regionList.getState().options.length; // 245The pipeline
Section titled “The pipeline”Every state change runs one pipeline over the base set and emits the result:
base set (cached per locale) -> regionFilter -> numberTypeFilter -> search -> sort -> prioritize -> emitThe base set is one RegionOption per region, computed once per locale and reused across
filter, search, and sort changes. Switching locale (localize) is the only mutation that rebuilds
it. A mutator that does not change the underlying value skips the whole pipeline and emits nothing.
Each option carries four fields:
type RegionOption<T = undefined> = { readonly region: RegionCode; // ISO code, e.g. 'US' readonly callingCode: string; // numeric calling code as string, e.g. '1' readonly displayName: string; // localized name from Intl.DisplayNames readonly data: T; // your per-option payload, or undefined};displayName comes from the runtime’s Intl.DisplayNames, so the exact string depends on the
platform’s ICU data and is not byte-stable across environments.
Reading and subscribing
Section titled “Reading and subscribing”Construction does not emit. Read the bootstrap value with getState(), then subscribe for
changes. subscribe returns an unsubscribe function.
const regionList = createRegionList();
renderOptions(regionList.getState().options);const unsubscribe = regionList.subscribe((state) => renderOptions(state.options));The emitted RegionListState carries the current options plus the active regionFilter,
numberTypeFilter, searchQuery, and locale.
Filtering
Section titled “Filtering”setRegionFilter restricts the list to a set of regions; setNumberTypeFilter restricts it to
regions supporting at least one of the given number types. Both accept null to remove the
restriction. [] is distinct from null: it matches nothing, which lets a caller express “user
cleared the filter” versus “narrowed to zero”. Passing an array with the same contents in the same
order is a no-op.
regionList.setRegionFilter(['US', 'CA', 'GB']);regionList.getState().options.length; // 3
regionList.setNumberTypeFilter(['TOLL_FREE']);regionList.getState().options.map((option) => option.region); // ['US', 'CA', 'GB']
regionList.setRegionFilter(null); // all regions supporting TOLL_FREEregionList.setNumberTypeFilter(null); // all 245 regionsPass either filter at construction with regionFilter / numberTypeFilter. These are the same
values createPhoneInput accepts, so a picker can offer exactly the regions the input resolves.
See Input and picker sync.
Search
Section titled “Search”search(query) filters by the query and re-emits. An empty or whitespace-only query is
short-circuited: the filter is skipped entirely and the full set passes through.
The default matcher tests, in order, the localized displayName, the region code, and the
callingCode. It is diacritic- and case-insensitive: it normalizes both sides with Unicode NFD,
strips combining marks, and lowercases, so "osterreich" matches "Österreich". A leading + on
the query is stripped before the calling-code test, so "+44" and "44" both match the United
Kingdom.
regionList.search('united');// matches United States, United Kingdom, United Arab Emirates, ...
regionList.search('+380');regionList.getState().options.map((option) => option.region); // ['UA']To change what a match means, pass a searchFn. It receives the raw query and one option and
returns true to keep it. The library still short-circuits empty and whitespace-only queries and
never calls your function for them, so you own only the non-empty case. A matcher that searches by
calling code exactly:
const regionList = createRegionList({ searchFn: (query, option) => option.callingCode === query.replace(/^\+/, ''),});
regionList.search('+1');regionList.getState().options.map((option) => option.region);// every region on calling code 1: US, CA, and the North American Numbering Plan territoriessort sets the order. Built-in comparators collate displayName in the list’s own locale (not
the host default) and break every tie by region code, so they are total orders: the same input
produces the same order run to run within one runtime.
sort |
Order |
|---|---|
'alphabetical' |
by displayName, then region code (default) |
'callingCode' |
by numeric calling code, then displayName, then region code |
| a function | your comparator; you own its determinism |
const byCallingCode = createRegionList({ sort: 'callingCode' });byCallingCode .getState() .options.slice(0, 3) .map((option) => option.callingCode);// ['1', '1', '1']: calling code 1 sorts first, then 7, 20, ...
const byRegionCode = createRegionList({ sort: (a, b) => a.region.localeCompare(b.region),});Because displayName and collation both come from the runtime’s ICU, the alphabetical order is
locale- and ICU-dependent and not guaranteed identical across environments.
Prioritizing regions
Section titled “Prioritizing regions”prioritize pins regions to the top, in the order given, ahead of the sorted rest. A pinned
region that a filter or search has removed does not reappear; prioritization reorders the surviving
options, it does not add any.
const regionList = createRegionList({ sort: 'alphabetical', prioritize: ['US', 'GB', 'UA'],});
regionList .getState() .options.slice(0, 5) .map((option) => option.region);// ['US', 'GB', 'UA', 'AF', 'AX']: pinned three first, then alphabetical by nameprioritize is a construction option and does not change at runtime.
Localization
Section titled “Localization”locale selects the language for every displayName, resolved through Intl.DisplayNames.
localize(nextLocale) switches it, rebuilds the base set, recomputes every name and every data
value, then re-runs the pipeline. It is a no-op when the locale is unchanged. The default is
'en'.
const regionList = createRegionList({ locale: 'fr' });regionList.getState().options.find((option) => option.region === 'DE')?.displayName; // 'Allemagne'
regionList.localize('en');regionList.getState().options.find((option) => option.region === 'DE')?.displayName; // 'Germany'When the runtime cannot produce a localized name for a region, displayName falls back to the ISO
region code.
Per-option data
Section titled “Per-option data”dataFactory fills the data slot on each option. It runs once per region per base
recomputation (construction and localize), receives the region’s already-computed
region, callingCode, displayName, and the active locale, and returns your payload. Its
return type flows into the list’s generic T, so data is typed for every downstream consumer.
Keep it pure; it must not read mutable external state unless you call refresh when that state
changes.
import { createRegionList, regionToFlagEmoji } from '@telixon/web-sdk';
const regionList = createRegionList({ dataFactory: ({ region, callingCode }) => ({ flag: regionToFlagEmoji(region), dialPrefix: `+${callingCode}`, }),});
const us = regionList.getState().options.find((option) => option.region === 'US');us?.data; // { flag: '🇺🇸', dialPrefix: '+1' }refresh recomputes the base set (display names and data) and always emits, even when nothing
changed. Use it when external state your dataFactory reads has moved.
Rendering with flag emoji
Section titled “Rendering with flag emoji”regionToFlagEmoji maps a region code to its flag by returning the two Unicode regional indicator
symbols for its letters. regionToFlagEmoji('US') returns '🇺🇸'. Rendering is the platform’s
choice: systems with flag-emoji support show a flag, and systems without it (notably Windows) show
the two letters. For a flag on every platform, render an SVG set keyed by the region code instead.
import { regionToFlagEmoji, type RegionOption } from '@telixon/web-sdk';
function renderOptions(options: readonly RegionOption[]): void { select.replaceChildren( ...options.map((option) => { const element = document.createElement('option'); element.value = option.region; element.textContent = `${regionToFlagEmoji(option.region)} ${option.displayName} +${option.callingCode}`; return element; }), );}Lifecycle
Section titled “Lifecycle”destroy clears all subscribers and is idempotent. Call it when the picker unmounts.
Related
Section titled “Related”- Filters shares
regionFilterandnumberTypeFilterwith the input. - Input and picker sync wires the picker to a phone input.
- Region list reference lists every option, method, and type.