Skip to content
weftGitHub

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/warp

Usage

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 tel input, so phones show the keypad and SMS autofill works.
  • The field says how many digits are missing when people leave it.

API Reference

PropTypeDefaultDescription
labelstring—The visible name, and what screen readers announce.
hintunknown—A short line of help under the field.
errorunknown—Replaces the hint and marks the field invalid.
iconIconRef—Leading icon: it sweeps with the border.
prefixunknown—Text or an element inside the field before the value, such as $.
suffixunknown—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.
clearablebooleanfalseA clear button (shown once there is a value).
counternumber—Maximum length, shown as a counter.
idstring—The element id. One is made for you if you leave it out.
disabledbooleanfalseCan't be focused or changed.
readonlybooleanfalseShows the value and lets it be selected and copied, but not changed.
requiredbooleanfalseThe form will not submit without it.
namestring—Posts the value with a form.
localestring—The language and region used to format values.
todaystring—Pins "today" (server render, tests); the browser's clock otherwise.
presentationPresentation—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.
countrystring—The selected country, when you hold it yourself.
defaultCountrystring—The first country. Follows the page's locale otherwise.
countriesstring[]—Limits the list to these countries.
EventTypeDefaultDescription
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

RelatedAddress fieldCountry selectInput