Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Inputs

Country Select

Inputs

Country Select React component for Tailwind CSS v4

ComboboxCron Picker
Live props
Field
label
description
error
required
Behavior
multiple
search
clearable
highlightMatches
display
maxSelectedNo limit
maxVisibleChipsFit
wrap
closeOnSelect
name
autoComplete
State
defaultValue
invalid
disabled
readOnly
Appearance
showFlag
showDialCode
size
variant
radius
Content
locale
placeholder
clearLabel
Layout
defaultOpen
responsive
sheetTitle
side
align
maxHeight360px
virtualThreshold100
Code
import { CountrySelect } from "@/components/ui/country-select"

<CountrySelect />
  1. Home
  2. Components
  3. Inputs
  4. Country Select
Pro

Country Select React component for Tailwind CSS v4

v1.0.0·Added 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-select

Country 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 ""
    ReactNode

    The label above the field; plain text also titles the phone sheet.

  • descriptiondefault ""
    ReactNode

    Helper text under the field, linked with aria-describedby.

  • errordefault ""
    ReactNode

    Replaces the description with an error and marks the field invalid.

  • requireddefault false
    boolean

    Adds the asterisk to the label and blocks an empty native form submit.

  • multipledefault false
    boolean

    Pick several countries: value becomes a string[] of codes, shown as chips with flags.

  • value
    string | 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[]) => void

    Called with the new code (or the list of codes with multiple) after a pick, a chip removal, a clear or browser autofill.

  • showFlagdefault true
    boolean

    The emoji flag in each row, in the field and in chips. Windows has no flag emoji, so it shows a small code chip there.

  • showDialCodedefault false
    boolean

    The calling code ("+1") at the end of each row and after the chosen name. Dial codes are searchable either way.

  • priority
    string[]

    Codes listed first under labels.suggested, in this order. Default: the locale's country ("en-US" suggests US). [] removes the group.

  • countries
    string[]

    The codes to offer, e.g. the countries you ship to. Default: every country libphonenumber-js knows (about 245).

  • exclude
    string[]

    Codes to leave out of the list.

  • localedefault ""
    string

    The 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) => ReactNode

    Your own flag for a code (an svg flag set). Default: the CountryFlag emoji.

  • placeholderdefault ""
    string

    Shown while nothing is chosen. Default "Select a country", or "Select countries" with multiple.

  • labels
    Partial<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.

  • clearabledefault false
    boolean

    An x in the field while a value is set; it clears the value (all countries with multiple).

  • clearLabeldefault "Clear"
    string

    The accessible name of the clear button.

  • highlightMatchesdefault true
    boolean

    Bold letters where a search matches a country name.

  • leftIcon
    ReactNode

    An 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.

  • invaliddefault false
    boolean

    The invalid look without a message.

  • disableddefault false
    boolean

    Dims the field and blocks it.

  • readOnlydefault false
    boolean

    Shows the country without letting anyone change it.

  • displaydefault "chips"
    "chips" | "count" | "text"

    With multiple: chips with flags, "3 selected", or the names joined.

  • maxSelecteddefault 0
    number

    With multiple: the most countries someone can pick (0 is no limit).

  • maxVisibleChipsdefault 0
    number

    With multiple: chips shown before "+N" (0 fits as many as one line holds).

  • wrapdefault false
    boolean

    With multiple: chips wrap onto more lines and the field grows.

  • closeOnSelectdefault false
    boolean

    With multiple: close the desktop list after each pick (the phone sheet waits for Done).

  • namedefault ""
    string

    Renders a hidden native select (hidden inputs with multiple) so forms submit the codes.

  • autoCompletedefault "country"
    string

    The hidden select's autocomplete token, so browser autofill picks the country from the saved address.

  • id
    string

    The trigger's id; the label points to it.

  • fieldProps
    Partial<FieldProps>

    Extra props for the Field wrapper (orientation, labelAction...).

  • defaultOpendefault false
    boolean

    Starts with the list open.

  • open
    boolean

    Controls the list's open state.

  • onOpenChange
    (open: boolean) => void

    Called when the list opens or closes.

  • responsivedefault true
    boolean

    A bottom sheet below 768px instead of the popover.

  • sheetTitledefault ""
    string

    The 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.

  • maxHeightdefault 360
    number

    The popover's tallest height in px (it also stops at the screen edge).

  • virtualThresholddefault 100
    number

    Above this many rows only the rows in view render, so the full list scrolls smoothly.

  • container
    HTMLElement | null

    Where the popover and sheet portal to. Default: document.body.

  • classNames
    Partial<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.

  • className
    string

    Merged onto the Field root when there is a label, description or error, otherwise onto the box.

  • ref
    RefObject<HTMLButtonElement | null> or (node) => void

    The trigger button; other button props (onBlur, aria-*, data-*, form) land there too.

Parts

  • CountryFlag

    component

    The 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*
      string

      An ISO alpha-2 code, "US".

    • sizedefault "md"
      "sm" | "md" | "lg"

      15, 17 or 19px.

    • fallback
      boolean

      Force the code chip (true) or the emoji (false). Default: the chip on Windows only.

  • getCountryOptions

    function

    The 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.

    • countries
      readonly string[]

      The codes to describe. Default: every country.

    • exclude
      readonly string[]

      Codes to leave out.

    • localedefault "en-US"
      string

      The language of the names and the sort order.

    • returns
      CountryOption[]

      One entry per country.

  • getCountryName

    function

    A country's name in a locale through Intl.DisplayNames: getCountryName("DE", "de-DE") gives "Deutschland". Unknown codes come back as the code.

    • code*
      string

      An ISO alpha-2 code.

    • localedefault "en-US"
      string

      The language of the name.

  • countryFromLocale

    function

    The 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

    function

    The emoji flag as a string from the two regional indicator letters: countryFlag("JP") gives 🇯🇵.

  • isCountryCode

    function

    True for a supported ISO alpha-2 code, in any case.

  • CountryOption

    type

    { code, name, dialCode, flag, keywords }: one country as getCountryOptions returns it.

  • GetCountryOptionsParams

    type

    The parameters of getCountryOptions.

  • CountryCode

    type

    The ISO alpha-2 code union from libphonenumber-js.

  • CountrySelectLabels

    type

    { search, empty, suggested, allCountries } plus the optional MultiSelect labels.

  • CountryFlagProps

    type

    The props of CountryFlag.

  • CountrySelectProps

    type

    The props of CountrySelect: single (value: string) or multiple (multiple: true, value: string[]).

  • CountrySelectSlot

    type

    The 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.

Dependencies

libphonenumber-jsselectmulti-selectfielduse-localeuse-controllable-stateui-config

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

SelectMulti SelectPhone InputTimezone SelectField

More React input components

Cron PickerCurrency InputDate PickerDate Time PickerEmail InputEmoji Picker
All 40 React input components

Articles using this component

  • React Country Select With Flags, Search and Dial Codes

    Component guides · Nov 6, 2026 · 9 min read

PreviousComboboxNext Cron Picker

Shipping address

Emma Carter · emma@northwind.com

Where we ship your order.

country="" · autocomplete="country" · virtual > 100 · bottom start · 360px

Try “germany”, “uk”, “+44” or “cote” · on phones: “Country” sheet