I9kPhoneInput β
Phone number input with a compact country-code picker: a flag-and-code face over a transparent native select that lists every country by name. Registers the number, not the picker, with a wrapping I9kField.
ts
import { I9kPhoneInput } from '@9klabs/design';Examples β
Inside I9kField β
vue
<I9kField label="WhatsApp number">
<I9kPhoneInput v-model="phone" v-model:country="country" country-label="Country code" />
</I9kField>Arabic country names β
vue
<I9kField label="Ψ±ΩΩ
ΩΨ§ΨͺΨ³Ψ§Ψ¨">
<I9kPhoneInput v-model="phone" v-model:country="country" country-label="Ω
ΩΨͺΨ§Ψ Ψ§ΩΨ―ΩΩΨ©" locale="ar" />
</I9kField>Error state β
That number doesn't look right.
vue
<I9kField label="WhatsApp number" error="That number doesn't look right.">
<I9kPhoneInput v-model="phone" v-model:country="country" country-label="Country code" />
</I9kField>Watch out β
- The component only collects the country and the typed number; it does not validate or normalise them.
- The server renders only the selected country; the named list appears after hydration.
- Inside an I9kField, do not pass id, aria-invalid, or aria-describedby β the field supplies them to the number.
- Use autocomplete="tel-national", not "tel": "tel" autofills the number with its country code, which the picker already shows.
- disabled freezes both the number and the country; readonly has no <select> equivalent and freezes only the number.
API β
Props β
| Prop | Type | Default | Required |
|---|---|---|---|
modelValue | string | β | yes |
country | string | β | yes |
countryLabel | string | β | yes |
locale | string | 'en' | no |
countryName | string | undefined | no |
uiSize | 'sm' | 'md' | 'lg' | undefined | no |
Emits β
| Event | Payload |
|---|---|
update:modelValue | [value: string] |
update:country | [country: string] |
Agent prompt β
Paste this into an agent to have it use the component correctly. It is self-contained.
text
Use I9kPhoneInput from @9klabs/design for a phone number that needs a country calling code, inside an I9kField.
import { I9kField, I9kPhoneInput } from '@9klabs/design';
Props:
- modelValue: string (required) β the number as typed, v-model target. The component does not parse or validate it; combine it with the country yourself (libphonenumber-js is a good fit).
- country: string (required) β ISO 3166-1 alpha-2 code, v-model:country target. Must be a key of I9K_COUNTRY_DIAL_CODES.
- countryLabel: string (required) β accessible name of the country select, e.g. "Country code".
- locale?: string β language the country names are listed in (default 'en'). Pass the page locale.
- countryName?: string β name attribute for the country select.
- uiSize?: 'sm' | 'md' | 'lg' β falls back to a wrapping I9kField's size, then to 'md'.
Emits: update:modelValue with the typed string; update:country with the picked code.
Behavior: inside an I9kField, only the number registers with the field, so the field's label, hint and error describe the number; the select is named by countryLabel. Other attributes (name, autocomplete, placeholder) go to the number input, which is type="tel"; class and style go to the wrapper. The control is always dir="ltr" so the code sits before the number in Arabic too. Standalone, give the number an accessible name with aria-label or aria-labelledby.
IMPORTANT: the named country list is rendered only after mount β the server renders just the selected code β because server and browser ICU name and sort countries differently and would otherwise hydrate out of step. Do not rely on the full option list in server-rendered HTML.
Usage:
<I9kField label="WhatsApp number"><I9kPhoneInput v-model="phone" v-model:country="country" country-label="Country code" name="phone" autocomplete="tel-national" /></I9kField>