Dots

Phone Field

A phone number input with a searchable calling code picker and a per-country mask.

We will text a 6-digit code to this number.
"use client"

import type { Country } from "@/components/ui/country-select"

The PhoneField component takes a phone number with its country: a calling code button that opens a searchable country list, and the national number formatted by the country mask as the user types. Use it for sign-up, two-step verification and paying a contact by phone. Pick it over TextField with type="tel" whenever the number may be international, since the country picker, the mask and paste handling come built in.

Installation

pnpm dlx shadcn@latest add @dots/phone-field

Usage

import type { Country } from "@/components/ui/country-select"
import { PhoneField } from "@/components/ui/phone-field"
const countries: Country[] = [
  { code: "GB", name: "United Kingdom", dialCode: "+44", phoneMask: "#### ######" },
  { code: "US", name: "United States", dialCode: "+1", phoneMask: "(###) ###-####" },
]
 
<PhoneField label="Phone number" countries={countries} defaultCountry="US" />

Features

  • A calling code button that opens the searchable CountrySelect list, with an optional popular group
  • The national number is formatted by the country's phoneMask as the user types, one # per digit
  • Typing or pasting a number that starts with a plus, such as +44 7700 900123, switches the country and keeps the national digits
  • A check at the end once the number fills the mask
  • value and onChange work with digits only, so you never parse the formatted string
  • With name, forms receive the full number with its calling code, such as +447700900123
  • The country button and the number share one invalid and disabled state

Countries

Each country needs a dialCode. Add a phoneMask to format the number and cap its length; without one the field takes up to 15 digits unformatted. The placeholder defaults to the mask with zeros. Pass the codes you expect most to popular to list them first.

const countries: Country[] = [
  { code: "DE", name: "Germany", dialCode: "+49", phoneMask: "### ########" },
  { code: "AE", name: "United Arab Emirates", dialCode: "+971", phoneMask: "## ### ####" },
  { code: "US", name: "United States", dialCode: "+1", phoneMask: "(###) ###-####" },
]

When two countries share a calling code, such as +1, the selected country wins if it is one of them. Otherwise the first country listed with that code is picked, so order countries with the most likely one first.

Complete

Once the national number fills the mask, the field shows a check at the end and gets data-complete. Set showComplete={false} to hide the check.

"use client"

import type { Country } from "@/components/ui/country-select"

Controlled

Control the country with country and onCountryChange, and the number with value and onChange. Both take plain values: a country code and the national digits. Try pasting +44 7700 900123 to see the country switch.

Type or paste +44 7700 900123 to switch the country.

+1

"use client"

import * as React from "react"

Invalid

Set isInvalid and pass an errorMessage that says how to fix the number. The country button and the number both take the invalid fill.

This number is too short for the United States.
"use client"

import type { Country } from "@/components/ui/country-select"

Disabled

Use isDisabled for a number that can't be changed here, such as a verified one. Say why in the description.

Verified. To change it, contact support.
"use client"

import type { Country } from "@/components/ui/country-select"

Accessibility

PhoneField is built on the React Aria TextField, so the label, description and error message are wired to the number input, and invalid and disabled states are announced. The country picker is a React Aria Select with a searchable list.

Label the field

Pass a visible label. Without one, pass aria-label so the number input has a name.

<PhoneField aria-label="Phone number" countries={countries} />

Name the country button

The country button shows only a calling code, so it is named by countryLabel, which defaults to "Country code". Translate it with the rest of your interface, along with searchLabel for the search field.

Input mode

The number input uses type="tel", inputMode="tel" and autoComplete="tel-national", so phones show the dial pad and browsers can fill a saved number.

API Reference

PhoneField

Accepts the React Aria TextField props except value, defaultValue, onChange, type, inputMode, children and className, which are replaced below.

PropTypeDefaultDescription
countriesCountry[]-Countries to pick the calling code from. Each needs dialCode; phoneMask formats the number.
popularstring[]-Country codes listed first.
countrystring-The selected country code (controlled), such as "GB".
defaultCountrystringFirst available countryThe selected country code (uncontrolled).
onCountryChange(code: string) => void-Called when the country changes, by picking or by typing a plus code.
valuestring-The national number as digits only (controlled), such as "7700900123".
defaultValuestring""The national number as digits only (uncontrolled).
onChange(digits: string) => void-Called with the national digits.
labelReactNode-The visible label. Without it, pass aria-label.
descriptionReactNode-Help text under the field.
errorMessageReactNode | (validation: ValidationResult) => ReactNode-Shown when the field is invalid.
placeholderstringThe mask with zerosPlaceholder for the number, such as "0000 000000".
showCompletebooleantrueShow a check at the end once the number fills the mask.
countryLabelstring"Country code"The accessible name of the country button.
searchLabelstring"Search country or code"The label of the search field in the country list.
emptyMessageReactNode-Shown when the search finds no country.
namestring-Adds a hidden input with the full number and calling code, such as "+447700900123".
isInvalidboolean-Marks the country button and the number as invalid.
isDisabledboolean-Disables the country button and the number.
classNamestring-Additional classes to apply to the root element.

Country

The country shape, exported from components/ui/country-select.

FieldTypeDefaultDescription
codestring-ISO 3166-1 alpha-2 code, such as "GB". Used as the key.
namestring-The name in the interface language.
dialCodestring-The calling code with the plus, such as "+44". Searchable.
phoneMaskstring-The national number mask, one # per digit.
flagReactNode-A monochrome flag. Without it the code is shown in a round chip.
isUnavailableboolean-Listed but not selectable.