# React Country Select With Flags, Search and Dial Codes

> Build a React country select with flags, names localized by the browser, search by name, code or +dial code, a suggested group, multiple picks and ISO values.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Nov 6, 2026
- Category: [Component guides](https://wingo-ui.com/blog/category/components)
- Reading time: 9 min
- Canonical: https://wingo-ui.com/blog/react-country-select

## TL;DR

A good React country select stores the ISO 3166-1 alpha-2 code (US, DE), shows each country with its flag and its name in the reader's language through Intl.DisplayNames, and searches by name, English name, code, dial code and common short names like UK or USA. Put the likely country on top, submit the code through a hidden native select so autofill works, use a bottom sheet on phones and a code chip where Windows has no flag emoji. The Wingo UI Country Select does all of this, with single or multiple selection.

A React country select looks like the easiest field on a checkout form. Then real users arrive: someone in Munich types "deutschland", someone in London types "UK", a Windows laptop shows "US" where the flag should be, and your database holds "United States", "USA" and "United States of America" for the same customer. This guide covers what the field needs to do, which npm packages exist, and how to use the Wingo UI [Country Select](https://wingo-ui.com/components/country-select) for a searchable country dropdown with flags, localized names, dial codes and multiple selection. It also covers when a [Phone Input](https://wingo-ui.com/components/phone-input) or a [Timezone Select](https://wingo-ui.com/components/timezone-select) is the field you actually want.

## What should a React country select do?

Seven things separate a country picker that takes a second from one people scroll through:

1. **Store the ISO code.** The value is `"US"` or `"DE"`. The name is presentation.
2. **Names in the reader's language.** A German visitor sees Deutschland, Frankreich and Vereinigte Staaten, sorted the German way.
3. **Search that forgives.** Match the localized name, the English name, the ISO code, the dial code (`+44`) and the short names people type (`USA`, `UK`, `Holland`), with accents ignored, so `cote` finds Côte d'Ivoire.
4. **The likely country first.** A "Suggested" group with the visitor's own country or your top markets, so most people never search.
5. **Flags that render everywhere.** Emoji flags are free, except on Windows, which shows letters.
6. **A real form value.** A hidden native `select` with `autocomplete="country"` so `FormData` gets the code and browser autofill fills it from a saved address.
7. **A phone layout.** About 245 rows in a popover anchored to a 44px field are hard to scroll with a thumb. A bottom sheet with the search pinned on top works better.

## Which React country select packages are there?

If you search npm for a react country dropdown with flags, four packages come up, each covering a different part of the job. As of November 6, 2026, from the npm registry and each README:

| Option | Latest release | What it gives you | Names |
|---|---|---|---|
| [react-flags-select](https://www.npmjs.com/package/react-flags-select) | 2.5.0, Jun 2025 | a select with SVG flags, search opt-in (`searchable`) | its bundled list, relabel with `customLabels` |
| [react-select-country-list](https://www.npmjs.com/package/react-select-country-list) | 2.2.3, Jan 2021 | label and value data for react-select | English |
| [react-country-region-selector](https://www.npmjs.com/package/react-country-region-selector) | 4.0.6, Feb 2026 | connected country and region dropdowns | from country-region-data |
| [i18n-iso-countries](https://www.npmjs.com/package/i18n-iso-countries) | 7.14.0, Feb 2025 | names in many languages, no UI | registered JSON per language |
| Wingo UI Country Select | copied source | searchable select, flags, dial codes, multiple, phone sheet | Intl.DisplayNames |

All four npm packages are MIT licensed. The Wingo UI component is part of the paid Pro plan. If you only need flags in a plain dropdown and want a free package, react-flags-select is the most direct fit. If you need regions or states under the country, react-country-region-selector does that and the Wingo UI component does not.

A shadcn country select is not in the official [shadcn/ui registry](https://ui.shadcn.com/r/index.json), which lists 65 UI components as of November 6, 2026 (it has `select`, `native-select` and `combobox`). Community versions exist; check what each one uses for names and flags before you copy it.

## How do you build a React country select with flag icons?

Install it with `npx wingo-ui@latest add country-select`, then render it with a label, a value and a few priority countries:

```tsx
"use client";

import { useState } from "react";
import { CountrySelect } from "@/components/ui/country-select";

export function ShippingCountry() {
  const [country, setCountry] = useState("US");

  return (
    <CountrySelect
      label="Country"
      description="Where we ship your order."
      value={country}
      onValueChange={setCountry}
      priority={["US", "CA", "MX"]}
      required
      name="country"
    />
  );
}
```

`label`, `description` and `error` render the shared [Field](https://wingo-ui.com/components/field) anatomy around the trigger, with the label tied to it and the hint linked through `aria-describedby`. Without `priority`, the suggested group holds the country of the locale, so `en-US` suggests the United States and `en-GB` the United Kingdom. Pass `priority={[]}` to drop the group.

> Live demo (Country Select): Open the list and type "germany", "uk", "+44" or "cote": each finds its country with the matched letters in bold. Try it at [Country Select](https://wingo-ui.com/components/country-select) and install it with `npx wingo-ui@latest add country-select`.

To limit the list to the countries you ship to, pass `countries={["US", "CA", "MX", "DE", "FR"]}`. To remove a few, pass `exclude`. With eight countries or fewer the search row hides itself (`search="auto"`); set `search="always"` or `"never"` to override that.

## How do country names get localized?

Older country packages ship a JSON file of English names, plus one more file per language you register. Browsers already know every country name in every major language through `Intl.DisplayNames`:

```ts
new Intl.DisplayNames(["de-DE"], { type: "region" }).of("DE"); // "Deutschland"
new Intl.DisplayNames(["fr-FR"], { type: "region" }).of("JP"); // "Japon"
```

The Country Select does this for you. Its `locale` prop sets the language of the names and the sort order. Without it, the component reads the locale from `UIProvider`, then from the browser. A German list sorts with a German collator, and the English names stay searchable, so a visitor who types "germany" in a German list still finds Deutschland.

The same helpers are exported for your own UI, such as an order summary:

```tsx
import { CountryFlag, getCountryName } from "@/components/ui/country-select";

export function ShipsTo({ code, locale }: { code: string; locale: string }) {
  return (
    <span className="inline-flex items-center gap-1.5">
      <CountryFlag code={code} size="sm" />
      {getCountryName(code, locale)}
    </span>
  );
}
```

One detail that bites in Next.js: Node and Chrome can ship different ICU data, so a few names differ between server and browser (Hong Kong and Macao are examples), and a server-rendered list of about 245 sorted names can fail hydration. The Country Select renders only the chosen country on the server and builds the full list after hydration, so the markup matches.

## Should you store the country name or the ISO code?

Store the code. Names depend on the language and change over time (Turkey is Türkiye in English now), while `"TR"` stays `"TR"`. Stripe's address `country` field and most carrier APIs take the ISO 3166-1 alpha-2 code.

With `name`, the component renders a hidden native `select` (one hidden input per country with `multiple`) that submits the code. Its `autoComplete` defaults to `"country"`, so when the browser fills a saved address, the picker updates too. Validate the code again on the server. `libphonenumber-js` is already a dependency of the component and exports `isSupportedCountry`:

```ts
// app/checkout/actions.ts
"use server";

import { isSupportedCountry } from "libphonenumber-js";

export async function saveAddress(formData: FormData) {
  const country = String(formData.get("country") ?? "").toUpperCase();
  if (!isSupportedCountry(country)) throw new Error("Unknown country");
  // store "US", show the name in the reader's locale when you render it
}
```

Then `<form action={saveAddress}>` around the `CountrySelect name="country"` is all the wiring a plain form needs.

## How do you show dial codes and pick several countries?

For a react country code dropdown, add `showDialCode`. The calling code (`+1`, `+44`) appears at the end of each row and after the chosen name. Dial codes are searchable with or without it.

For "Ships to" or "Markets" fields, add `multiple`. The value becomes a `string[]`, each pick adds a chip with its flag, and the list stays open while you pick:

```tsx
"use client";

import { useState } from "react";
import { CountrySelect } from "@/components/ui/country-select";

export function Markets() {
  const [markets, setMarkets] = useState<string[]>(["US", "CA"]);

  return (
    <CountrySelect
      multiple
      label="Ships to"
      description="Up to 5 countries on the Starter plan."
      value={markets}
      onValueChange={setMarkets}
      maxSelected={5}
      showDialCode
      clearable
      name="markets"
    />
  );
}
```

`maxSelected` caps the count, `display` switches between `chips`, `count` ("3 selected") and `text`, `maxVisibleChips` folds the rest into "+N", and `wrap` lets chips grow the field onto more lines. The multi mode is built on the [Multi-select](https://wingo-ui.com/components/multi-select), and the [React multi select guide](https://wingo-ui.com/blog/react-multi-select) covers chips, select all and limits in depth.

> Live demo (Country Select): Pick a few more countries: each adds a chip with its flag and the dial code shows in every row. Try it at [Country Select](https://wingo-ui.com/components/country-select) and install it with `npx wingo-ui@latest add country-select`.

## Why do flags show as letters on Windows?

An emoji flag is two regional indicator characters: `US` becomes 🇺🇸. macOS, iOS, Android and most Linux desktops draw them as flags. Windows ships an emoji font without national flags, so Chrome and Edge there draw the two letters (Firefox bundles its own emoji font with flags). On any Windows browser the Country Select swaps the emoji for a small code chip after mount, which reads better than two loose letters and looks the same in every browser. Force one or the other with `CountryFlag`'s `fallback` prop.

If your design needs real flags on every platform, pass your own SVG set through `renderFlag`:

```tsx
<CountrySelect
  label="Country"
  renderFlag={(code) => (
    <img src={`/flags/${code.toLowerCase()}.svg`} alt="" className="h-3.5 w-5 rounded-[2px] object-cover" />
  )}
/>
```

## How does it behave on phones and with a keyboard?

Below 768px the field opens a bottom sheet instead of a popover (`responsive`, default true). The title comes from the label, the search sits pinned under it, rows are 48px tall, and the list renders only the rows in view once it passes `virtualThreshold` (100), so scrolling about 245 countries stays smooth. On touch, `search="auto"` waits for a tap on the search row before it focuses, so the keyboard does not cover half the list the moment the sheet opens. With `multiple`, a Done button closes the sheet.

On a keyboard, typing a letter on the closed field opens the list with that letter in the search. Enter, Space or ArrowDown open it on the chosen country. Inside, the arrows move, PageUp and PageDown jump, Enter picks, and Escape closes and returns focus to the field.

## When is a phone input or a timezone select the better field?

A country select is the right field when the country is the data: a shipping or billing address, a tax residence, the markets a store sells to. Two neighbors often do the job better:

- **You want a phone number.** Do not put a country select with dial codes next to a text input. The [Phone Input](https://wingo-ui.com/components/phone-input) has the country button built in, formats the digits for that country as you type, switches the country when someone pastes `+44 7400 123456`, and returns one E.164 string. The [React phone input guide](https://wingo-ui.com/blog/react-phone-input) covers validation.
- **You want a time zone.** A country does not tell you the time zone (the United States has several). The [Timezone Select](https://wingo-ui.com/components/timezone-select) searches by city, abbreviation or offset, shows UTC offsets and offers "Use my time zone".

> Live demo (Phone Input): Paste a full number with a + prefix: the flag switches to that country and the field formats the rest. Try it at [Phone Input](https://wingo-ui.com/components/phone-input) and install it with `npx wingo-ui@latest add phone-input`.

On a checkout or sign-up form you often use two of them together: a Country Select in the address and a Phone Input under it, both on the same Field anatomy, so labels, errors and sizes line up. The [React form components guide](https://wingo-ui.com/blog/react-form-components-guide) covers that anatomy and the rest of the fields, and the [component guides category](https://wingo-ui.com/blog/category/components) has the other deep dives.

## Which pitfalls show up in production?

- **Saving the display name.** You end up mapping "Deutschland" back to `DE` in a migration. Save the code from day one.
- **Hardcoding `en-US`.** Visitors from other countries search in their own language. Leave `locale` empty to follow the browser or set it once on `UIProvider`.
- **A suggested group nobody asked for.** If the locale guess is wrong for your audience (an English browser in Germany suggests the US), pass your real top markets to `priority`.
- **A hand-rolled hidden input.** Without a native `select` that carries `autocomplete`, autofill fills the street and skips the country. Pass `name` and let the component render it.
- **A full list for three options.** If you deliver to three countries, `countries` with three codes (or a radio group) beats a searchable list of 245.

## How do you install it?

Run `npx wingo-ui@latest add country-select`. It copies the component with the Select, Multi-select and Field parts it uses and installs `libphonenumber-js` plus the packages those parts need. The Country Select is part of [Wingo UI Pro](https://wingo-ui.com/pricing) ($8 a month, $80 a year or $150 once), so sign in first with `npx wingo-ui@latest login`. The Field and the single Select are free with a free account, if you want to start with a short, fixed list of countries and upgrade later.

## Components in this post

- [Country Select](https://wingo-ui.com/components/country-select) (Pro): A searchable country picker with flags, localized names, optional dial codes, a suggested group and single or multiple selection. Install: `npx wingo-ui@latest add country-select`
- [Phone Input](https://wingo-ui.com/components/phone-input) (Pro): A React phone number input with a searchable country picker, formatting as you type, validation and one clean E.164 value out. Install: `npx wingo-ui@latest add phone-input`
- [Timezone Select](https://wingo-ui.com/components/timezone-select) (Pro): A searchable time zone picker with live UTC offsets, optional local times, region groups and a one-tap Use my time zone row. Install: `npx wingo-ui@latest add timezone-select`
- [Field](https://wingo-ui.com/components/field) (Free): A React form field wrapper every control shares: label, hint, animated error and counter, fieldsets, and the box recipe all inputs use. Install: `npx wingo-ui@latest add field`

## FAQ

### How do I make a React country select with flags?

Build the list from ISO 3166-1 alpha-2 codes, turn each code into its emoji flag from two regional indicator letters (or render an SVG flag), and get the name from Intl.DisplayNames in the reader's locale. Add a search row, put the likely country first and store the code as the value.

### Why do country flag emojis show as two letters on Windows?

Windows ships an emoji font without national flags, so Chrome and Edge on Windows draw the two regional indicator letters instead of the flag (Firefox bundles its own emoji font with flags). Detect Windows and show a small code chip, render SVG flags, or load a flag emoji polyfill font.

### Should I store the country name or the country code?

Store the ISO 3166-1 alpha-2 code, such as US or DE. Names change between languages and over time (Turkey became Türkiye), while the code stays stable and is what payment and shipping APIs such as Stripe expect.

### Does shadcn/ui have a country select?

No. As of November 6, 2026 the official shadcn/ui registry lists 65 UI components and none of them is a country select, so shadcn projects use a community component or build one on Select or Combobox.

### How do I localize country names in React without a translation file?

Use Intl.DisplayNames with type region: new Intl.DisplayNames(["de-DE"], { type: "region" }).of("DE") returns Deutschland. It is built into current browsers and Node, so you do not ship a JSON file of names per language.

### Is the Wingo UI Country Select free?

The Country Select, the Phone Input and the Timezone Select are part of Wingo UI Pro. The Field and the Select they build on are free.

---

Source: https://wingo-ui.com/blog/react-country-select
