Country Select React component for Tailwind CSS v4
v1.0.0Added in 1.14.0
A searchable country picker with flags, localized names, optional dial codes, a suggested group and single or multiple selection.
Click or tap the field and the list of about 245 countries opens, each row with its flag, its name in your locale and, with showDialCode, its calling code. Your own country (from the locale, or the priority codes you pass) sits on top under "Suggested". A search row on top filters as you type: by name in any language you search in ("deutschland" or "germany"), by ISO code ("US", "GB"), by dial code ("+40") or by a common short name ("USA", "UK", "Holland"), and accents never matter ("cote" finds Côte d'Ivoire, "turkiye" finds Türkiye), with the matching letters in bold. Keyboard: while closed, typing a letter opens the list with that letter in the search, Enter, Space or ArrowDown open it on the chosen country; while open, ArrowDown and ArrowUp move, PageUp and PageDown jump, Enter picks, Escape closes and focus returns to the field. With multiple, each pick adds a chip with its flag (an x on the chip removes it) and the list stays open; maxSelected caps the count. With clearable an x clears the value. On a phone the field opens a bottom sheet with the search pinned under the title and 48px rows; with multiple a Done button closes it. With name, a hidden native select (one hidden input per country with multiple) submits ISO codes and lets browser autofill fill the country.
Country Select is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add country-select, together with Select, Multi Select, Field, useLocale, useControllableState and 1 more and the npm package libphonenumber-js.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add country-selectCountry Select is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import CountrySelect from @/components/ui/country-select.
import { useState } from "react"
import { CountrySelect } from "@/components/ui/country-select"
export function ShippingCountry() {
const [country, setCountry] = useState("US")
const [markets, setMarkets] = useState<string[]>(["US", "CA"])
return (
<div className="flex flex-col gap-4">
<CountrySelect
label="Country"
description="Where we ship your order."
value={country}
onValueChange={setCountry}
priority={["US", "CA", "MX"]}
required
name="country"
/>
<CountrySelect
multiple
label="Markets"
value={markets}
onValueChange={setMarkets}
showDialCode
maxSelected={5}
clearable
/>
</div>
)
}Props
labeldefault""ReactNodeThe label above the field; plain text also titles the phone sheet.
descriptiondefault""ReactNodeHelper text under the field, linked with aria-describedby.
errordefault""ReactNodeReplaces the description with an error and marks the field invalid.
requireddefaultfalsebooleanAdds the asterisk to the label and blocks an empty native form submit.
multipledefaultfalsebooleanPick several countries: value becomes a string[] of codes, shown as chips with flags.
valuestring | string[]The chosen ISO 3166-1 alpha-2 code ("US"), or codes with multiple, when you control it. "" is nothing. Lowercase codes are read as uppercase.
defaultValuedefault""string | string[]The starting value while uncontrolled ([] with multiple). A form reset returns to it.
onValueChange(value: string) => void | (value: string[]) => voidCalled with the new code (or the list of codes with multiple) after a pick, a chip removal, a clear or browser autofill.
showFlagdefaulttruebooleanThe emoji flag in each row, in the field and in chips. Windows has no flag emoji, so it shows a small code chip there.
showDialCodedefaultfalsebooleanThe calling code ("+1") at the end of each row and after the chosen name. Dial codes are searchable either way.
prioritystring[]Codes listed first under labels.suggested, in this order. Default: the locale's country ("en-US" suggests US). [] removes the group.
countriesstring[]The codes to offer, e.g. the countries you ship to. Default: every country libphonenumber-js knows (about 245).
excludestring[]Codes to leave out of the list.
localedefault""stringThe language of the names and their sort order, through Intl.DisplayNames ("de-DE" lists Deutschland). Default: the UIProvider's locale, else the browser's. English names stay searchable in other locales.
renderFlag(code: CountryCode) => ReactNodeYour own flag for a code (an svg flag set). Default: the CountryFlag emoji.
placeholderdefault""stringShown while nothing is chosen. Default "Select a country", or "Select countries" with multiple.
labelsPartial<CountrySelectLabels>Strings: search ("Search by name or code"), empty ("No country found"), suggested ("Suggested"), allCountries ("All countries"), plus the multi-select strings (selectAll, clearAll, done, selected, limit, more, remove) and the screen reader result count.
searchdefault"auto""auto" | "always" | "never"The search row. auto shows it above 8 countries and, on touch, waits for a tap so the keyboard does not cover the list; always focuses it on open everywhere.
clearabledefaultfalsebooleanAn x in the field while a value is set; it clears the value (all countries with multiple).
clearLabeldefault"Clear"stringThe accessible name of the clear button.
highlightMatchesdefaulttruebooleanBold letters where a search matches a country name.
leftIconReactNodeAn icon at the start of the field: while nothing is chosen (or with showFlag off) in single mode, always in front of the chips with multiple.
sizedefault"md""sm" | "md" | "lg"32, 40 (44 on touch) or 48px, like every field and button.
variantdefault"soft""soft" | "outline" | "underline" | "ghost"The field box look.
radiusdefault"md""none" | "sm" | "md" | "lg" | "full"The corner radius, scaled per size like the button.
invaliddefaultfalsebooleanThe invalid look without a message.
disableddefaultfalsebooleanDims the field and blocks it.
readOnlydefaultfalsebooleanShows the country without letting anyone change it.
displaydefault"chips""chips" | "count" | "text"With multiple: chips with flags, "3 selected", or the names joined.
maxSelecteddefault0numberWith multiple: the most countries someone can pick (0 is no limit).
maxVisibleChipsdefault0numberWith multiple: chips shown before "+N" (0 fits as many as one line holds).
wrapdefaultfalsebooleanWith multiple: chips wrap onto more lines and the field grows.
closeOnSelectdefaultfalsebooleanWith multiple: close the desktop list after each pick (the phone sheet waits for Done).
namedefault""stringRenders a hidden native select (hidden inputs with multiple) so forms submit the codes.
autoCompletedefault"country"stringThe hidden select's autocomplete token, so browser autofill picks the country from the saved address.
idstringThe trigger's id; the label points to it.
fieldPropsPartial<FieldProps>Extra props for the Field wrapper (orientation, labelAction...).
defaultOpendefaultfalsebooleanStarts with the list open.
openbooleanControls the list's open state.
onOpenChange(open: boolean) => voidCalled when the list opens or closes.
responsivedefaulttruebooleanA bottom sheet below 768px instead of the popover.
sheetTitledefault""stringThe phone sheet title. Default: the label text, else "Country" ("Countries" with multiple).
sidedefault"bottom""top" | "right" | "bottom" | "left"Where the popover opens; it flips when there is no room.
aligndefault"start""start" | "center" | "end"How the popover lines up with the field.
maxHeightdefault360numberThe popover's tallest height in px (it also stops at the screen edge).
virtualThresholddefault100numberAbove this many rows only the rows in view render, so the full list scrolls smoothly.
containerHTMLElement | nullWhere the popover and sheet portal to. Default: document.body.
classNamesPartial<Record<"field" | "label" | "description" | "error" | "counter" | "box" | "trigger" | "value" | "placeholder" | "icon" | "chevron" | "clear" | "content" | "search" | "list" | "item" | "group" | "empty" | "chips" | "chip" | "overflow" | "summary" | "footer" | "flag" | "dialCode", string>>Extra classes for the inner parts: the Select (or MultiSelect) slots plus flag and dialCode.
classNamestringMerged onto the Field root when there is a label, description or error, otherwise onto the box.
refRefObject<HTMLButtonElement | null> or (node) => voidThe trigger button; other button props (onBlur, aria-*, data-*, form) land there too.
Parts
CountryFlag
componentThe emoji flag for a code, or a small code chip on Windows (which has no flag emoji). Use it in your own rows and summaries. Takes span props.
code*stringAn ISO alpha-2 code, "US".
sizedefault"md""sm" | "md" | "lg"15, 17 or 19px.
fallbackbooleanForce the code chip (true) or the emoji (false). Default: the chip on Windows only.
getCountryOptions
functionThe list the component uses: every country with its localized name, dial code, emoji flag and search keywords, sorted by name in the locale. Reuse it for a table, a filter or a server-side list.
countriesreadonly string[]The codes to describe. Default: every country.
excludereadonly string[]Codes to leave out.
localedefault"en-US"stringThe language of the names and the sort order.
returnsCountryOption[]One entry per country.
getCountryName
functionA country's name in a locale through Intl.DisplayNames: getCountryName("DE", "de-DE") gives "Deutschland". Unknown codes come back as the code.
code*stringAn ISO alpha-2 code.
localedefault"en-US"stringThe language of the name.
countryFromLocale
functionThe country a locale points to, with likely subtags: "en-US" gives US, "ro" gives RO, "" when there is none. The default priority uses it.
countryFlag
functionThe emoji flag as a string from the two regional indicator letters: countryFlag("JP") gives 🇯🇵.
isCountryCode
functionTrue for a supported ISO alpha-2 code, in any case.
CountryOption
type{ code, name, dialCode, flag, keywords }: one country as getCountryOptions returns it.
GetCountryOptionsParams
typeThe parameters of getCountryOptions.
CountryCode
typeThe ISO alpha-2 code union from libphonenumber-js.
CountrySelectLabels
type{ search, empty, suggested, allCountries } plus the optional MultiSelect labels.
CountryFlagProps
typeThe props of CountryFlag.
CountrySelectProps
typeThe props of CountrySelect: single (value: string) or multiple (multiple: true, value: string[]).
CountrySelectSlot
typeThe keys of classNames.
CountrySelectSize
type"sm" | "md" | "lg".
CountrySelectVariant
type"soft" | "outline" | "underline" | "ghost".
CountrySelectRadius
type"none" | "sm" | "md" | "lg" | "full".
CountrySelectSide
type"top" | "right" | "bottom" | "left".
CountrySelectAlign
type"start" | "center" | "end".
Examples
With dial codes
showDialCode adds the calling code to each row and after the chosen name; “+44” finds the UK.
Several countries
multiple shows chips with flags, maxSelected caps the count and clearable empties it.
A short list
countries limits the choice, priority={[]} drops the suggested group and the search hides below 9 rows.
Localized names
locale renders and sorts names through Intl.DisplayNames; English names still match a search.
Sizes, variants and radii
sm, md and lg line up with every field and button.
In a checkout form
required, a validation error and a hidden native select that browser autofill can fill.
Files
- components/ui/country-select.tsx
FAQ
Is the React Country Select component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Country Select?
Run npx wingo-ui@latest add country-select. It also adds the Wingo UI items it builds on (Select, Multi Select, Field, useLocale, useControllableState and 1 more) and the npm package libphonenumber-js. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/country-select.
How does Country Select behave on phones?
A bottom sheet below 768px instead of the popover. The responsive prop controls this and is on by default.
See also
More React input components
Articles using this component
- React Country Select With Flags, Search and Dial Codes
Component guides9 min read