PhoneField
A phone number with a country picker. Formats as people type and stores the full international number.
SourcePackage @weft-js/warp
Preview
tsx
import { state } from '@weft-js/core'import { PhoneField, Stack, Text } from '@weft-js/warp'import type { E164 } from '@weft-js/warp-core'export function Basic() { const phone = state<E164 | null>(null) return ( <Stack gap="related"> <PhoneField label="Mobile number" hint="We'll text you when a deploy fails." value={phone} /> <Text subtle>{phone.value ? `Saved as ${phone.value}` : 'Not a full number yet'}</Text> </Stack> )}Installation
npm install @weft-js/warpUsage
tsx
import { PhoneField } from '@weft-js/warp'A short country list
countries limits the list. A pasted +49… picks its country.
tsx
import { PhoneField } from '@weft-js/warp'export function Countries() { return ( <PhoneField label="Mobile number" defaultCountry="DE" countries={TEAM_COUNTRIES} defaultValue="+4915112345678" /> )}const TEAM_COUNTRIES = ['US', 'GB', 'DE', 'FR', 'SE', 'JP', 'IN', 'BR']States
For a number people can see but not change, show a read-only Input.
tsx
import { Grid, PhoneField, Stack, Text } from '@weft-js/warp'export function States() { return ( <Grid cols={3} gap="group"> <Cell name="Empty"><PhoneField label="Mobile number" /></Cell> <Cell name="Filled"><PhoneField label="Mobile number" defaultValue="+14155550123" /></Cell> <Cell name="Hint"><PhoneField label="Mobile number" hint="We'll text a code." /></Cell> <Cell name="Invalid"><PhoneField label="Mobile number" defaultValue="+14155550123" error="This number is already on another account." /></Cell> <Cell name="Disabled"><PhoneField label="Mobile number" defaultValue="+14155550123" disabled /></Cell> <Cell name="Read-only"><PhoneField label="Mobile number" defaultValue="+14155550123" readonly /></Cell> </Grid> )}const Cell = (p: { name: string; children?: unknown }) => ( <Stack gap="tight"> <Text role="caption" subtle>{p.name}</Text> {p.children} </Stack>)When to use
Use it for
- Any phone number you plan to call or text
Reach for something else
- Input, when it's an extension or an internal number
Accessibility
- Tab
- Moves from the country button to the number
- A native
telinput, so phones show the keypad and SMS autofill works. - The field says how many digits are missing when people leave it.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | The visible name, and what screen readers announce. |
hint | unknown | — | A short line of help under the field. |
error | unknown | — | Replaces the hint and marks the field invalid. |
icon | IconRef | — | Leading icon: it sweeps with the border. |
prefix | unknown | — | Text or an element inside the field before the value, such as $. |
suffix | unknown | — | Text or an element inside the field after the value, such as kg. |
variant | "floating" | "classic" | — | floating (default, or the app's warp({ fields })) · classic: the label above. |
appearance | "line" | — | line: only a bottom line under the value, no box or fill. The boxed field otherwise. |
size | "sm" | "md" | "lg" | 'md' | Control size. md by default. |
clearable | boolean | false | A clear button (shown once there is a value). |
counter | number | — | Maximum length, shown as a counter. |
id | string | — | The element id. One is made for you if you leave it out. |
disabled | boolean | false | Can't be focused or changed. |
readonly | boolean | false | Shows the value and lets it be selected and copied, but not changed. |
required | boolean | false | The form will not submit without it. |
name | string | — | Posts the value with a form. |
locale | string | — | The language and region used to format values. |
today | string | — | Pins "today" (server render, tests); the browser's clock otherwise. |
presentation | Presentation | — | auto opens a popover, or a sheet when the viewport is under 600px wide; popover and sheet force one. |
value | `+${number}` | State<`+${number}` | null> | null | — | The number as +14155550123, or null until it's complete. Can be a State. |
defaultValue | `+${number}` | null | — | The starting value, when the component owns the state. |
country | string | — | The selected country, when you hold it yourself. |
defaultCountry | string | — | The first country. Follows the page's locale otherwise. |
countries | string[] | — | Limits the list to these countries. |
| Event | Type | Default | Description |
|---|---|---|---|
onInput | ((e: InputEvent & { currentTarget: HTMLInputElement & HTMLTextAreaElemen… | — | Called on each keystroke. |
onValueChange | ((v: `+${number}` | null, reason: ChangeReason) => void) | — | Called with each new value, and whether a pointer, a key or code made the change. |
onCountryChange | ((c: string) => void) | — | Hears the new country. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="phone-field"] [data-part="body"]
bodycountryinput