Skip to content
WANCRAFT

F.203Forms & Inputs

Phone Input

An international phone field with a searchable country list, as-you-type formatting and paste-to-detect E.164 numbers.

NewReactTailwindadvanced

Overview

Phone Input pairs a country selector with a tel field. The selector opens a searchable listbox (an ARIA combobox in a native popover) of about thirty common regions, each shown as a two-letter region badge rather than an emoji flag, with names from Intl.DisplayNames in the locale you pass. The number formats as you type with a simple national pattern while the caret stays on the same digit, Backspace over a space or dash deletes the digit before it, and a leading trunk 0 is dropped. Typing, pasting or autofilling a +E.164 number selects its country automatically. onChange reports { country, national, e164, complete }, the field uses autocomplete="tel" and inputMode="tel", and errors are wired through aria-invalid and aria-describedby. The bundled dataset is a display aid, not a numbering-plan database or a validation library.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/phone-input.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/phone-input.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/phone-input.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { PhoneInput } from "@/components/ui/phone-input";

export function PhoneInputExample() {
  return (
    <div className="w-full max-w-sm">
      <PhoneInput name="phone" onChange={(phone) => console.log(phone.e164)} />
    </div>
  );
}

03Props

PropTypeDefaultDescription
value / defaultValuestring—Controlled number in E.164 ("+60123456789"), or the initial one when uncontrolled.
defaultCountrystring"MY"ISO region selected when no number implies one.
onChange(value: PhoneInputValue) => void—Called after every edit with { country, national, e164, complete }; e164 is "" while empty.
countriesPhoneCountry[]PHONE_COUNTRIESRegions as { code, dial, patterns, trunk? } — patterns use # for digits. The default list is ≈30 common regions, not exhaustive.
labelstring"Phone number"Visible <label> for the tel input.
hintReactNode—Helper text, linked with aria-describedby.
errorstring—External error; sets aria-invalid and overrides the built-in checks (unknown calling code, incomplete number on blur).
localestring"en"Locale for region names (Intl.DisplayNames) and list sorting (Intl.Collator).
formatAsYouTypebooleantrueInsert spaces, dashes and brackets from the country pattern while typing.
showDialCodebooleantrueShow +code next to the region badge in the selector.
accentstring"#FF7448"Focus ring, caret, selection and the completion tick.
disabled / requiredbooleanfalseNative disabled / required on the input.
namestring—Submits the E.164 value in a hidden input.
inputIdstring—id of the tel input (generated when omitted).

04Features

  • 01Searchable country listbox in a native popover: filter by name, ISO code or +calling code
  • 02Region badges drawn as two-letter codes — no emoji flags and no flag images
  • 03As-you-type national formatting that keeps the caret on the same digit; Backspace over a separator deletes the digit before it
  • 04Typing, pasting or autofilling +E.164 selects the country (shared codes such as +1 keep the current region)
  • 05onChange returns { country, national, e164, complete }; a hidden input submits E.164 in native forms
  • 06Region names and ordering follow the locale through Intl.DisplayNames and Intl.Collator
input · keyboardinput · focusinput · click

05Accessibility

  • The tel input has a real <label>, autocomplete="tel" and inputMode="tel"; hint and error text are linked with aria-describedby, and errors set aria-invalid and are announced politely.
  • The country button names the current region and code ("Country: Malaysia, +60. Change country") with aria-haspopup="listbox" and aria-expanded.
  • The search field is an ARIA combobox that drives the listbox with aria-activedescendant: Up / Down, Page Up / Down, Home / End move, Enter selects, Escape closes only the list and returns focus to the button.
  • Alt + Down Arrow in the number field opens the country list; choosing a country returns focus to the number.
  • Country changes, including ones detected from a pasted number, are announced in a polite live region.
  • Region badges are aria-hidden; the region name is always given in text.

06Limitations

  • The bundled patterns are simplified display formats for about thirty regions. They do not know area codes, number types or valid ranges — this is not a validation library; use libphonenumber or a server check when correctness matters.
  • Regions sharing a calling code (+1 US / Canada) are not distinguished from the digits; the current or first listed region wins.