Phone Input

International phone field with a searchable country picker (emoji flags, dial codes, ~45 built-in countries or your own list), per-country grouping applied as you type without losing the caret, E.164 output with { country, national, valid }, paste-a-full-+number country detection, and a blur-time length check that shakes the field with an inline error.

Preview

Phone Input preview width

Install

Add it with the shadcn CLI, or copy the source manually.

$ bunx --bun shadcn add @karaui/phone-input
@karaui not found? Add it to components.json
{
  "registries": {
    "@karaui": "https://karaui.kamisama-v0.my.id/r/{name}.json"
  }
}

@karaui is in shadcn's public registry directory, so the latest CLI needs no setup. On an older CLI, merge this into the existing file once and every @karaui/… install resolves.

API Reference

value?
string

Controlled E.164 value, e.g. "+6281234567890". Empty string for none.

—
defaultValue?
string
—
onChange?
((e164: string, meta: PhoneValueMeta) => void)

E.164 string (empty when there are no digits) plus parsed details.

—
country?
string

Controlled ISO country code.

—
defaultCountry?
string

Initial ISO country code. Default "ID".

ID
onCountryChange?
((country: PhoneCountry) => void)
—
countries?
PhoneCountry[]

Replace the built-in country list.

[ { code: "ID", name: "Indonesia", dial: "62", pattern: "### #### #####", length: [9, 12] }, { code: "US", name: "United States", dial: "1", pattern: "(###) ###-####", length: [10, 10] }, { code: "GB", name: "United Kingdom", dial: "44", pattern: "#### ######", length: [9, 10] }, { code: "IN", name: "India", dial: "91", pattern: "##### #####", length: [10, 10] }, { code: "SG", name: "Singapore", dial: "65", pattern: "#### ####", length: [8, 8] }, { code: "MY", name: "Malaysia", dial: "60", pattern: "##-#### ####", length: [9, 10] }, { code: "JP", name: "Japan", dial: "81", pattern: "##-####-####", length: [9, 10] }, { code: "DE", name: "Germany", dial: "49", pattern: "### ########", length: [7, 12] }, { code: "BR", name: "Brazil", dial: "55", pattern: "(##) #####-####", length: [10, 11] }, { code: "AU", name: "Australia", dial: "61", pattern: "### ### ###", length: [9, 9] }, { code: "CA", name: "Canada", dial: "1", pattern: "(###) ###-####", length: [10, 10] }, { code: "MX", name: "Mexico", dial: "52", pattern: "## #### ####", length: [10, 10] }, { code: "FR", name: "France", dial: "33", pattern: "# ## ## ## ##", length: [9, 9] }, { code: "IT", name: "Italy", dial: "39", pattern: "### ### ####", length: [6, 11], keepLeadingZero: true }, { code: "ES", name: "Spain", dial: "34", pattern: "### ## ## ##", length: [9, 9] }, { code: "NL", name: "Netherlands", dial: "31", pattern: "# ########", length: [9, 9] }, { code: "BE", name: "Belgium", dial: "32", pattern: "### ## ## ##", length: [8, 9] }, { code: "CH", name: "Switzerland", dial: "41", pattern: "## ### ## ##", length: [9, 9] }, { code: "SE", name: "Sweden", dial: "46", pattern: "## ### ## ##", length: [7, 9] }, { code: "NO", name: "Norway", dial: "47", pattern: "### ## ###", length: [8, 8] }, { code: "DK", name: "Denmark", dial: "45", pattern: "## ## ## ##", length: [8, 8] }, { code: "IE", name: "Ireland", dial: "353", pattern: "## ### ####", length: [7, 9] }, { code: "PT", name: "Portugal", dial: "351", pattern: "### ### ###", length: [9, 9] }, { code: "PL", name: "Poland", dial: "48", pattern: "### ### ###", length: [9, 9] }, { code: "RU", name: "Russia", dial: "7", pattern: "### ###-##-##", length: [10, 10] }, { code: "TR", name: "Türkiye", dial: "90", pattern: "### ### ## ##", length: [10, 10] }, { code: "CN", name: "China", dial: "86", pattern: "### #### ####", length: [11, 11] }, { code: "HK", name: "Hong Kong", dial: "852", pattern: "#### ####", length: [8, 8] }, { code: "TW", name: "Taiwan", dial: "886", pattern: "### ### ###", length: [9, 9] }, { code: "KR", name: "South Korea", dial: "82", pattern: "##-####-####", length: [9, 10] }, { code: "TH", name: "Thailand", dial: "66", pattern: "## ### ####", length: [8, 9] }, { code: "VN", name: "Vietnam", dial: "84", pattern: "## ### ## ##", length: [9, 10] }, { code: "PH", name: "Philippines", dial: "63", pattern: "### ### ####", length: [10, 10] }, { code: "PK", name: "Pakistan", dial: "92", pattern: "### #######", length: [10, 10] }, { code: "BD", name: "Bangladesh", dial: "880", pattern: "####-######", length: [10, 10] }, { code: "AE", name: "United Arab Emirates", dial: "971", pattern: "## ### ####", length: [8, 9] }, { code: "SA", name: "Saudi Arabia", dial: "966", pattern: "## ### ####", length: [9, 9] }, { code: "EG", name: "Egypt", dial: "20", pattern: "### ### ####", length: [10, 10] }, { code: "NG", name: "Nigeria", dial: "234", pattern: "### ### ####", length: [10, 10] }, { code: "ZA", name: "South Africa", dial: "27", pattern: "## ### ####", length: [9, 9] }, { code: "KE", name: "Kenya", dial: "254", pattern: "### ######", length: [9, 9] }, { code: "AR", name: "Argentina", dial: "54", pattern: "## ####-####", length: [10, 11] }, { code: "CO", name: "Colombia", dial: "57", pattern: "### ### ####", length: [10, 10] }, { code: "CL", name: "Chile", dial: "56", pattern: "# #### ####", length: [9, 9] }, { code: "NZ", name: "New Zealand", dial: "64", pattern: "## ### ####", length: [8, 10] }, ]
label?
string
—
placeholder?
string
—
error?
string | boolean

Override the built-in "invalid number" message, or force an error.

—
hint?
string
—
disabled?
boolean
false
name?
string
—
id?
string
—
aria-label?
string
—
onBlur?
(() => void)
—
className?
string
—

Updated