# react-native-country-picker-modal: A Searchable, Themeable Country Picker

> This React Native component renders a modal list of countries with flags, calling codes, currencies and regions, adds fuzzy search and an A to Z jump index, and bundles 18 name translations with a fallback to English. It has no native code, so it runs in Expo Go as well as in a bare app.

**xcarpentier/react-native-country-picker-modal** — 🇦🇶 Country picker provides a modal allowing a user to select a country from a list. It display a flag next to each country name.

- Repository: https://github.com/xcarpentier/react-native-country-picker-modal
- Website: https://reactnative.gallery/xcarpentier/country-picker
- Stars: 1,113 · Forks: 829
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/xcarpentier-react-native-country-picker-modal

## A Drop-In Modal With the Country List Already Written

The component is described as a searchable, themeable country picker for React Native, running on iOS, Android and Web, and the older project description calls it a country picker that provides a modal allowing a user to select a country from a list and displays a flag next to each country name.

The usage is short. You keep the selected country code in state, you render the CountryPicker with the props you want, and the onSelect callback gives you the full country object:

```tsx
import { useState } from 'react'
import { SafeAreaView, Text } from 'react-native'
import CountryPicker, {
  type Country,
  type CountryCode,
} from 'react-native-country-picker-modal'

export default function App() {
  const [countryCode, setCountryCode] = useState<CountryCode>('US')
  const [country, setCountry] = useState<Country>()

  return (
    <SafeAreaView>
      <CountryPicker
        countryCode={countryCode}
        withFilter
        withFlag
        withCallingCode
        onSelect={(selected) => {
          setCountryCode(selected.cca2)
          setCountry(selected)
        }}
      />
      {country ? <Text>You picked {country.name as string}</Text> : null}
    </SafeAreaView>
  )
}
```

Tapping the flag opens the modal. If you would rather drive it yourself, you pass visible and handle onOpen and onClose.

The separation between the two pieces of state is deliberate. countryCode is the narrow type the component takes as a prop, and country is the wide object it hands back, so an application can hold the code for rendering and the object for the details it needs.

## The Data Set: 250 Countries and Their Metadata

The component ships 250 countries with names, flags, calling codes, currencies, regions and subregions, and that data set is the part of the library that saves the most work.

A country picker written from scratch needs a table of countries, and the table needs to agree with whatever standard your application already uses, because a country code that means one thing in your backend and another in the picker is a data bug that only shows up for a handful of users. Shipping the list inside the package removes that class of problem.

The typing is what makes the data usable from TypeScript. The component is written in TypeScript and the country codes, regions and translations are literal union types, so a mistyped country code is a compile error rather than a row that silently never matches.

The data is not fetched at runtime. There is a data/ directory in the repository, and the package.json carries two scripts that maintain it, data:encode which runs node ./scripts/encode-countries.js and data:minify which runs node ./scripts/minify.js against the countries emoji JSON file. So the pipeline is a script that produces a JSON file, which is then minified, and the result is what ships in the package.

The practical consequence is that the list is a snapshot. It is accurate as of the build, and a country added to the standard afterwards is not in the picker until the package is updated.

## Emoji Flags With No Download at Runtime

The flag feature is the one with the most obvious appeal, and the implementation choice behind it is the interesting part.

Emoji flags with no runtime dependency, derived from the country code, nothing to download. The flag is the regional indicator emoji pair for the country code, which the operating system already knows how to render, so there is no image bundle, no asset pipeline and no network request.

That matters more than it first sounds. A picker built on image flags needs either a bundled sprite sheet per locale or a flag CDN, and the second option means the picker makes network requests, which is exactly the kind of dependency that breaks on a slow connection and that has to be handled in a privacy review.

The alternative is offered too. The withEmoji prop defaults to true, and when it is false, images are fetched, with the README pointing at an Image flags section for the details of that path. So the default is the dependency free one and the image flags are the opt in alternative.

## Fuzzy Search, an A to Z Index and 18 Translations

Three list features are worth separating, because they solve different problems.

Fuzzy search across name, country code and calling code, plus an A to Z jump index. The fuzzy search is over three fields rather than one, which is what makes it usable in practice, since a user who knows the calling code and not the country name is the common case in a phone number form. The A to Z jump index is the withAlphaFilter prop, and it is the shortcut for a user who knows the name but not the spelling, which is the other common case.

The 18 name translations are bundled, with automatic fallback to English. The translation prop takes a language code and defaults to 'common', and the README says it falls back to English per country, which is the detail that makes this work in practice, since a translation set rarely covers every country, and a picker that showed a blank name for the missing ones would be worse than one that showed English.

There is a filtering prop set as well. countryCodes restricts the list to a set, excludeCountries removes some, preferredCountries shows some first, and region and subregion restrict by geography, with the note that preferredCountries is ignored when withAlphaFilter is true, because the two ways of organising a long list conflict and the jump index wins.

## Theming and the Built-in Dark Mode

The component is fully themeable, with a built-in dark theme, and that is stated as a feature rather than left to the props table.

The props that control the list are mostly switches. withFlag defaults to true and shows a flag on each row. withEmoji defaults to true and selects the emoji or image implementation. withCallingCode and withCurrency default to false and append the calling code and the currency to each row. withFilter defaults to false and shows the search field, and withAlphaFilter defaults to false and shows the A to Z index.

So a picker can be as plain as a list of names, or as dense as name, flag, calling code and currency on every row, and the search field and the jump index are opt in rather than always present, which is the right default for a list of 250 entries that is usually opened for a second.

The default of withFilter being false is worth noticing. The search field is off until you ask for it, on the reasoning that most of the time the user is looking for a country they already know the name of, and a permanent search box on top of the modal pushes the list down.

## React 19 for the v3 Line and v2 as the Escape Hatch

The requirements are stated as a table, and the compatibility note is the most important line in the README.

The requirements are react at 19.0 or later and react-native at 0.78 or later. And the note reads: using React 18 or React Native 0.72? Stay on v2, with a link to a migrating from v2 section.

That is a significant jump. The v2 line was published in July 2020 and the v1.11.0 in June 2020, and the v3.0.0 arrived on 2026-09-02. So the modern line is a rewrite for the current React Native, with the type system and the data pipeline rebuilt, and the older line is the fallback for applications that cannot move yet.

The version history explains the scale of the change. From v1.11.0 in 2020 and v2.0.0 in 2020 to v3.0.0 in 2026 is six years and a major version, and the release of v3.0.0 in September 2026 suggests the rewrite has been in progress for a while.

For an evaluation, the practical question is which React Native your application is on. If it is 0.78 or later, the v3 line is the one to read. If it is older, the v2 line still exists, and the README keeps a migration section for it.

## The Build, the Data Scripts and the Example Application

The packaging is a modern library layout, and the details tell you how the component is meant to be consumed.

There is no native code and no linking step, so it works in Expo Go as well as bare React Native, and that is the single most important practical fact for anyone trying the component. The build is done with bob, the module entry points are separate for commonjs and ESM, and the types entry points are separate for each, with the export map naming a declaration file for each entry point.

The published files list excludes the tests, the fixtures, the mocks and the source maps, and sideEffects is false, which lets a bundler drop the module entirely when nothing imports it.

The test command sets TZ=Europe/Paris, which is the kind of detail that matters in a component with date and locale handling, and there is a verify script that runs lint, typecheck, test and build in sequence. The repository has a.circleci directory, an eslint flat config, a prettier configuration, a babel config, and a scripts/ directory holding the two data pipeline scripts.

The example application is a full React Native project with its own metro config, an app.json and an App.tsx, and the README points at example/App.tsx as a runnable version exercising every option, which is the fastest way to see what a prop combination looks like before writing any of it yourself.

## Conclusion

react-native-country-picker-modal fits a React Native app that needs a country or phone code field and does not want to maintain a country list, a flag asset pipeline and a search implementation of its own, since the component ships all three and exposes them as props. It is a poor fit if your app is on React 18 or React Native 0.72, where the v3 line does not apply and the v2 line is the one to use. Before upgrading, read the migration notes, and note that the data set is baked into the package, so a country added to the ISO list is not available until the package is updated. The component is MIT licensed, the current version is 3.0.0 from 2026-09-02, and the last push was on 2026-10-13.

## FAQ

### What does react-native-country-picker-modal provide?

It is a searchable, themeable country picker modal for React Native on iOS, Android and Web, shipping 250 countries with names, flags, calling codes, currencies, regions and subregions, 18 name translations with a fallback to English, fuzzy search and an A to Z jump index.

### Does the country picker need native code or a linking step?

No. There is no native code and no linking step, so it works in Expo Go as well as in bare React Native, which is the main practical reason to reach for it rather than building a picker with native dependencies.

### Which React and React Native versions does version 3 need?

React 19.0 or later and React Native 0.78 or later. The README says that if you are on React 18 or React Native 0.72 you should stay on v2, and there is a migrating from v2 section for that upgrade path.

### How are the country flags rendered?

By default with emoji flags derived from the country code, with no runtime dependency and nothing to download. Setting withEmoji to false switches to image flags, which are fetched, and the README points at an Image flags section for the details of that path.

### How do I install the country picker?

With npm, yarn, pnpm or bun, for example `npm install react-native-country-picker-modal`. The package is MIT licensed, the current version is 3.0.0 from 2026-09-02, and the last push was on 2026-10-13.

## Sources

- [License: MIT](https://github.com/xcarpentier/react-native-country-picker-modal/blob/master/LICENSE)
- [Project website](https://reactnative.gallery/xcarpentier/country-picker)
- [README](https://github.com/xcarpentier/react-native-country-picker-modal/blob/master/README.md)
- [Releases](https://github.com/xcarpentier/react-native-country-picker-modal/releases)
- [xcarpentier/react-native-country-picker-modal on GitHub](https://github.com/xcarpentier/react-native-country-picker-modal)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/xcarpentier-react-native-country-picker-modal
