Input and picker sync
A phone input and a region picker are two views of one region. This guide wires them so a picker selection drives the input, and a region the input auto-detects from the digits drives the picker. Both are headless controllers, so the wiring is two subscriptions and one call each way. Read Region picker and the input tutorial first if either controller is new.
The two directions
Section titled “The two directions”The picker and the input each own a region and expose one lever to set it:
| Direction | Trigger | Call |
|---|---|---|
| Picker to input | user selects a region | phoneInput.setRegion(region) |
| Input to picker | input resolves a region from digits | read state.region, set picker’s selected value |
phoneInput.setRegion(region) switches the active region and re-resolves the current digits under
its rules. It pushes a new history entry, so it is undoable. state.region is the region the input
resolved for the digits currently typed, or null when nothing has resolved yet.
Picker to input
Section titled “Picker to input”When the user picks a region, call setRegion. The input re-formats and re-validates whatever is
already typed against the new region and emits one state.
regionSelect.addEventListener('change', () => { phoneInput.setRegion(regionSelect.value as RegionCode);});setRegion always resolves to the region you pass; the picker is the source of truth for that
call. It does not read the picker’s options list, so a region absent from a filtered picker can
still be set if you pass it. Keep the value you send inside the picker’s filter to avoid a
selection the picker cannot display.
Input to picker
Section titled “Input to picker”In international mode the input reads the calling code from the digits and resolves the region on
its own. Typing +44 20 7946 0958 resolves state.region to 'GB'; typing +380 67 123 4567
resolves it to 'UA'. When that happens you have not touched the picker, so reflect the new region
back onto it in the input’s subscription:
phoneInput.subscribe((state) => { if (state.region !== null && state.region !== regionSelect.value) { regionSelect.value = state.region; }});Guard against a feedback loop: only assign when the value actually differs. Setting a
<select>’s value does not fire change, so this assignment does not re-enter the picker to
input path; if your picker emits on programmatic selection, apply the same “skip when unchanged”
guard there.
state.region is null before any calling code completes and whenever the digits do not resolve
under the active filters. Leave the picker on its last value in that case rather than clearing it,
so a half-typed number does not blank the selection.
The strict-mode caveat
Section titled “The strict-mode caveat”strict mode changes what the input reports as its region, and that changes what the picker sees.
Without strict, the input resolves digits to their actual region: a Canadian number typed into a
US-defaulted input reports state.region === 'CA', and the input-to-picker subscription moves the
picker to Canada. With strict: true, the input keeps its configured region for numbers that share
a calling code: the same Canadian number reports state.region === 'US', and the picker stays on
the United States. Under strict, that Canadian number is also invalid for the United States, so
state.validationError is { kind: 'PATTERN_MISMATCH' }.
Decide which you want before wiring the input-to-picker direction. If the picker is the user’s
declared region and must not drift, use strict and let the picker stay put. If the input should
follow wherever the digits actually belong, leave strict off. See
Strict mode.
End-to-end wiring
Section titled “End-to-end wiring”One international input with a synced picker. The picker offers the regions the input accepts by
sharing the same regionFilter, and each option renders its flag through regionToFlagEmoji.
import { ensureEngineReady, type RegionCode } from '@telixon/core';import { createPhoneInput, createRegionList, regionToFlagEmoji } from '@telixon/web-sdk';
await ensureEngineReady();
const inputElement = document.querySelector<HTMLInputElement>('#phone')!;const regionSelect = document.querySelector<HTMLSelectElement>('#region')!;
const allowedRegions: readonly RegionCode[] = ['US', 'CA', 'GB', 'UA'];
const phoneInput = createPhoneInput({ mode: 'international', input: inputElement, regionFilter: allowedRegions, display: { callingCodeInInput: true, plusPrefix: 'fixed' },});
const regionList = createRegionList({ regionFilter: allowedRegions, sort: 'alphabetical',});
// Render and re-render the picker from region-list state.function renderPicker(options: readonly { region: RegionCode; displayName: string; callingCode: string }[]): void { regionSelect.replaceChildren( ...options.map((option) => { const element = document.createElement('option'); element.value = option.region; element.textContent = `${regionToFlagEmoji(option.region)} ${option.displayName} +${option.callingCode}`; return element; }), );}
renderPicker(regionList.getState().options);regionList.subscribe((state) => renderPicker(state.options));
// Picker to input.regionSelect.addEventListener('change', () => { phoneInput.setRegion(regionSelect.value as RegionCode);});
// Input to picker: follow an auto-detected region without re-entering the change handler.phoneInput.subscribe((state) => { if (state.region !== null && state.region !== regionSelect.value) { regionSelect.value = state.region; }});Typing +380 67 123 4567 resolves state.region to 'UA' and moves the picker to Ukraine;
choosing United Kingdom in the picker calls setRegion('GB') and re-resolves the digits under
United Kingdom rules. Both controllers stay in agreement in either direction.
Lifecycle
Section titled “Lifecycle”Call phoneInput.destroy() and regionList.destroy() when the field unmounts. destroy on the
input detaches its DOM listeners; on the region list it clears subscribers. Both are idempotent.
Related
Section titled “Related”- Region picker covers the picker on its own.
- Filters explains sharing
regionFilterandnumberTypeFilter. - Programmatic control covers driving the input from code.