PhoneNumberField

A phone number input widget that combines country dialing-code selection, phone number formatting, and validation feedback in one reusable field. It uses @adaptavant/libphonenumber for parsing and as-you-type formatting, and exposes label, placeholder, country search, error, warning, size, and variant props so product surfaces can keep phone entry consistent without rebuilding the same field logic. variant="subtle" renders the inline ghost treatment.

Quick Start

Installation
npm install @adaptavant/phone-number-field
Import
import { PhoneNumberField } from '@adaptavant/phone-number-field';

Key Features​

The PhoneNumberField component provides a complete phone entry experience:

  • Country selection — Shows a dialing-code dropdown with searchable country options.
  • As-you-type formatting — Formats the phone number as the user enters digits based on the selected country.
  • Initial value sync — Accepts e164, countryCode, and countryName through phoneNumberProps so existing contact data can hydrate the field.
  • Validation feedback — Supports error and warning states through isErrorField, isWarningField, and errorMessage.
  • Localized labels — Lets consumers customize the country dropdown label, search copy, input label, and placeholder.
  • Change callbacks — Emits structured phone data when the number changes or the country is selected.

Basic Usage​

Pass phoneNumberProps to initialize the selected country and phone number. The e164 value is formatted for display using the supplied country information.

, +1
return (
	<PhoneNumberField
		phoneNumberProps={{
			countryCode: 'US',
			countryName: 'United States',
			e164: '+15034524242',
			countryCodeProps: {
				label: 'Country',
			},
			inputProps: {
				label: 'Phone number',
			},
		}}
	/>
);

Inline variant​

Set variant="subtle" for the inline ghost treatment. Both controls stay borderless at rest, pick up a neutral background on hover, and show a border on focus. size still applies. Field messages stay on the phone input.

, +61
return (
	<PhoneNumberField
		errorMessage="Enter a valid phone number"
		phoneNumberProps={{
			countryCode: 'AU',
			countryName: 'Australia',
			countryCodeProps: {
				label: 'Country',
			},
			inputProps: {
				label: 'Phone number',
				placeholder: 'Phone number',
			},
		}}
		variant="subtle"
	/>
);

Empty Field​

Omit e164 when the field should start empty. Provide the country details to control which dialing code is selected by default.

, +1
return (
	<PhoneNumberField
		phoneNumberProps={{
			countryCode: 'US',
			countryName: 'United States',
			countryCodeProps: {
				label: 'Country',
			},
			inputProps: {
				label: 'Phone number',
				placeholder: 'Enter your phone number',
			},
		}}
	/>
);

With Custom Labels​

Use nested countryCodeProps and inputProps to customize field labels, search text, fallback copy, and the phone input placeholder.

, +1
return (
	<PhoneNumberField
		phoneNumberProps={{
			countryCode: 'US',
			countryName: 'United States',
			countryCodeProps: {
				label: 'Dialing code',
				searchPlaceholder: 'Find a country',
				searchResultFallback: 'No matching country',
			},
			inputProps: {
				label: 'Mobile number',
				placeholder: 'Add mobile number',
			},
		}}
	/>
);

With Error Message​

Set isErrorField with errorMessage when the field should display an error state. The same errorMessage text is also used for warning states when isWarningField is set.

, +1
return (
	<PhoneNumberField
		errorMessage="Enter a valid phone number"
		isErrorField
		phoneNumberProps={{
			countryCode: 'US',
			countryName: 'United States',
			e164: '+1503',
			countryCodeProps: {
				label: 'Country',
			},
			inputProps: {
				label: 'Phone number',
			},
		}}
	/>
);

Large Size​

Use size="large" when the phone field needs to align with larger form controls.

, +1
return (
	<PhoneNumberField
		phoneNumberProps={{
			countryCode: 'US',
			countryName: 'United States',
			e164: '+15034524242',
			countryCodeProps: {
				label: 'Country',
			},
			inputProps: {
				label: 'Phone number',
			},
		}}
		size="large"
	/>
);

formatPhoneNumberByLocation — National format​

Formats a phone number into national format when the number's country matches the user's geolocation.

US number · US geolocation → (646) 980-4127UK number · GB geolocation → 020 7183 8750
return (
	<Box className="flex flex-col gap-3">
		<Text className="text-body-14">
			US number · US geolocation →{' '}
			{formatPhoneNumberByLocation('+16469804127', 'US')}
		</Text>
		<Text className="text-body-14">
			UK number · GB geolocation →{' '}
			{formatPhoneNumberByLocation('+442071838750', 'GB')}
		</Text>
	</Box>
);

formatPhoneNumberByLocation — International format​

Formats a phone number into international format when the number's country does not match the user's geolocation.

US number · GB geolocation → +1 646-980-4127UK number · US geolocation → +44 20 7183 8750
return (
	<Box className="flex flex-col gap-3">
		<Text className="text-body-14">
			US number · GB geolocation →{' '}
			{formatPhoneNumberByLocation('+16469804127', 'GB')}
		</Text>
		<Text className="text-body-14">
			UK number · US geolocation →{' '}
			{formatPhoneNumberByLocation('+442071838750', 'US')}
		</Text>
	</Box>
);

formatPhoneNumberByLocation — Edge cases​

Empty or invalid inputs are returned unchanged. Typical usage is displaying a stored E.164 number in a UI based on the user's location.

Empty input → ""Invalid input → notvalid(646) 980-4127
function ContactCard({ phone, userCountry }) {
	const displayPhone = formatPhoneNumberByLocation(phone, userCountry);
	return <Text className="text-body-14">{displayPhone}</Text>;
}

return (
	<Box className="flex flex-col gap-3">
		<Text className="text-body-14">
			Empty input → "{formatPhoneNumberByLocation('', 'US')}"
		</Text>
		<Text className="text-body-14">
			Invalid input → {formatPhoneNumberByLocation('notvalid', 'US')}
		</Text>
		<ContactCard phone="+16469804127" userCountry="US" />
	</Box>
);

With End Adornment​

Use inputProps.adornmentEnd to render trailing content inside the phone number input, such as a copy affordance. The node sits inside the input's focus container, so it shares the field's hover surface instead of being a separate target. Styling, interactivity, and accessibility of the node are the consumer's responsibility.

, +1
​
return (
	<PhoneNumberField
		phoneNumberProps={{
			countryCode: 'US',
			countryName: 'United States',
			e164: '+15034524242',
			countryCodeProps: {
				label: 'Country',
			},
			inputProps: {
				label: 'Phone number',
				adornmentEnd: (
					<IconButton
						aria-label="Copy phone number"
						className="me-2"
						icon={ClipboardCopyIcon}
						size="small"
						variant="neutralTertiary"
					/>
				),
			},
		}}
	/>
);

API Reference​

PhoneNumberField

Props

PropDefaultDescription
variant?'standard''standard' | 'subtle'
standard keeps the outlined country dropdown and phone input. subtle is the inline ghost treatment: no border at rest, a neutral background on hover, and a border on focus. It uses the existing InlineField wrapper. The country column is 100px below the lg breakpoint and 80px from lg up at size="standard", and 100px at size="large". Error and warning messages stay on the phone input. Labels stay available to assistive technology and are visually hidden.
children?undefinedReact.ReactNode
Optional content rendered before the country selector and phone input row.
phoneNumberProps?{}PhoneNumberPropsSync
Initial phone value and nested props for the country dropdown and phone text input.
phoneNumberProps.e164?undefinedstring
Phone number in E.164 format. Used to populate and format the visible phone input.
phoneNumberProps.countryCode?undefinedstring
ISO region code for the selected country, for example 'US', 'IN', or 'GB'.
phoneNumberProps.countryName?undefinedstring
Display name of the selected country, used to resolve the dialing code option.
phoneNumberProps.errorMessage?undefinedstring
Error text associated with the phone value. Used when the component syncs validation state from props.
phoneNumberProps.countryCodeProps?undefinedCountryCodeDropdownProps
Labels and fallback copy for the country dialing-code dropdown.
phoneNumberProps.countryCodeProps.label?undefinedstring
Label shown above the country dialing-code dropdown.
phoneNumberProps.countryCodeProps.searchPlaceholder?'Search countries'string
Label text for the country search field inside the dropdown.
phoneNumberProps.countryCodeProps.searchResultFallback?'No matching results'string
Message shown when the country search has no matching results.
phoneNumberProps.inputProps?undefinedPhoneTextInputProps
Labels and placeholder copy for the phone number text input.
phoneNumberProps.inputProps.label?undefinedstring
Label shown above the phone number input.
phoneNumberProps.inputProps.placeholder?'Enter your phone number'string
Placeholder shown in the phone number input.
phoneNumberProps.inputProps.adornmentEnd?undefinedReact.ReactNode
Content rendered at the trailing end of the phone number input, inside its focus container so it shares the field's hover surface. The consumer owns the node's styling, interactivity, and accessibility.
errorMessage?''string
Field message shown when the component is in an error or warning state.
isErrorField?falseboolean
Forces the phone input into an error state and displays errorMessage.
isWarningField?falseboolean
Shows the field message in a warning state.
onPhoneInputOnChangeProps?undefined(event: PhoneInputEvent) => void
Callback fired when the phone number changes or a country is selected. The event includes formatted phone values, region code, validity, and callback type.
size?'standard''standard' | 'large'
Sets the EDS field size for the country dropdown and phone input. Applies in both standard and subtle variants.
className?''string
Custom class applied to the root element.
classNames?{}Partial<Record<'root' | 'fieldRow' | 'phoneInputWrapper', string>>
Slot-level class names for the root, field row, and phone input wrapper.
style?{}React.CSSProperties
Inline style applied to the root element.
styles?{}Partial<Record<'root' | 'fieldRow' | 'phoneInputWrapper', React.CSSProperties>>
Slot-level inline styles for the root, field row, and phone input wrapper.

Callback event types

TypeDataDescription
PHONE_NUMBER_INPUT_CHANGEPhoneInputChangeCallbackDataFired when the phone input value changes.
COUNTRY_SELECTPhoneInputChangeCallbackData & { dialingCode: string; countryName: string }Fired when a country option is selected from the dropdown.

Style API​

Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.

Please refer to the Style API documentation for more insights.