react-native-paper-country-picker
v1.2.7
Published
A customizable React Native Paper country picker with searchable modal, flags, and dialing codes.
Maintainers
Readme
react-native-paper-country-picker
A customizable React Native Paper country picker with:
- 🌍 195 countries with flags (via
react-native-country-flag) - 🔍 Searchable modal — search by name (EN or ES), ISO code, dial code, or currency code
- 📞 Dial code + phone input — flag + code selector with an editable phone number field
- 💱 Currency support — ISO 4217 codes + symbols for all countries
- 🌐 Bilingual — English and Spanish names, UI strings in both languages
- 🇬🇹 Default country — pre-select any country on mount (defaults to Guatemala)
- ⭐ Preferred countries pinned to the top of the list
- 🚫 Excluded countries support
- 🎛
displayMode— configure what the input shows after selection - 🖊
inputMode—'outlined'(border) or'flat'(underline) for all display modes - 🎨 Fully customizable — style overrides, custom row renderer, custom input renderer
- ♿ Full accessibility labels
- 💙 Built on top of
react-native-paper— matches your app's Material theme out of the box
Preview
Installation
npm install react-native-paper-country-picker react-native-country-flag
# or
yarn add react-native-paper-country-picker react-native-country-flagPeer dependencies
Make sure you have these installed in your project:
npm install react-native-paper
react-native-paperrequires wrapping your app in a<PaperProvider>. See the react-native-paper docs.
Usage
Basic — Country selector
import { useState } from 'react';
import { CountryPickerInput, type Country } from 'react-native-paper-country-picker';
export default function MyForm() {
const [country, setCountry] = useState<Country | null>(null);
return (
<CountryPickerInput
label="Country"
value={country}
onSelect={setCountry}
helperText="Select your country of residence"
/>
);
}Default country (Guatemala pre-selected)
Use defaultCountry to pre-select a country when the component mounts and no value is provided. Defaults to 'GT' (Guatemala).
<CountryPickerInput
value={country}
onSelect={setCountry}
defaultCountry="GT" // ISO code — any country from the list
/>displayMode="dialCode" — Phone input with editable number
Shows the flag + dial code selector on the left and an editable phone number TextInput on the right. Guatemala is pre-selected by default.
const [country, setCountry] = useState<Country | null>(null);
const [phone, setPhone] = useState('');
<CountryPickerInput
displayMode="dialCode"
defaultCountry="GT"
value={country}
onSelect={setCountry}
phoneNumber={phone}
onChangePhoneNumber={setPhone}
preferredCountries={['GT', 'MX', 'SV', 'US']}
language="es"
/>
// Full number: country.dialCode + ' ' + phone → "+502 5555 5555"Phone input props
| Prop | Type | Description |
|------|------|-------------|
| phoneNumber | string | Controlled value for the phone number field |
| onChangePhoneNumber | (phone: string) => void | Callback when the number changes |
| phonePlaceholder | string | Placeholder text for the phone number field |
| phoneInputProps | TextInputProps (partial) | Extra props forwarded to the phone TextInput (e.g. maxLength, keyboardType) |
displayMode="currency" — Currency selector
Show the flag, currency symbol and ISO 4217 code.
const [currency, setCurrency] = useState<Country | null>(null);
<CountryPickerInput
displayMode="currency"
value={currency}
onSelect={setCurrency}
/>
// After selecting Guatemala → shows: 🇬🇹 Q GTQ
// After selecting United States → shows: 🇺🇸 $ USD
// After selecting Germany → shows: 🇩🇪 € EURinputMode — flat or outlined
Controls the visual style of the input field. Works for all displayMode values (name, dialCode, currency).
// Outlined (default) — border all around
<CountryPickerInput
inputMode="outlined"
value={country}
onSelect={setCountry}
/>
// Flat — underline only, no border radius
<CountryPickerInput
inputMode="flat"
displayMode="dialCode"
value={country}
onSelect={setCountry}
phoneNumber={phone}
onChangePhoneNumber={setPhone}
/>| Value | Appearance |
|-------|-----------|
| 'outlined' (default) | Full border + borderRadius: 4, selector background #F5F5F5 |
| 'flat' | Bottom border only, no radius, white background throughout |
Spanish language
<CountryPickerInput
language="es"
displayMode="dialCode"
value={country}
onSelect={setCountry}
preferredCountries={['GT', 'MX', 'SV']}
/>
// Label → "Prefijo telefónico"
// Placeholder → "Selecciona prefijo"
// Country names → in SpanishPreferred and excluded countries
<CountryPickerInput
value={country}
onSelect={setCountry}
preferredCountries={['GT', 'MX', 'SV', 'US']}
excludedCountries={['KP', 'CU']}
/>Custom styles
Use the styles prop to override individual sections of the component:
import { type CountryPickerStyles } from 'react-native-paper-country-picker';
const darkStyles: CountryPickerStyles = {
phoneInputContainer: {
borderColor: '#30363D',
borderRadius: 8,
backgroundColor: '#161B22',
},
dialCodeSelector: {
backgroundColor: '#0D1117',
borderRightColor: '#30363D',
},
dialCodeText: { color: '#58A6FF', fontWeight: '700' },
phoneInput: { color: '#E6EDF3' },
modal: { backgroundColor: '#161B22', borderWidth: 1, borderColor: '#30363D' },
modalTitle: { color: '#E6EDF3' },
};
<CountryPickerInput
displayMode="dialCode"
defaultCountry="GT"
value={country}
onSelect={setCountry}
styles={darkStyles}
/>CountryPickerStyles reference
| Key | Type | Targets |
|-----|------|---------|
| container | ViewStyle | Outer wrapper of the whole component |
| modal | ViewStyle | The picker modal container |
| modalTitle | TextStyle | Modal title text |
| searchInput | ViewStyle | Search TextInput inside the modal |
| countryRow | ViewStyle | Each country row in the modal list |
| phoneInputContainer | ViewStyle | Outer row for flag+code+number (dialCode mode) |
| dialCodeSelector | ViewStyle | The pressable flag+code button |
| dialCodeText | TextStyle | The +502 code label |
| phoneInput | TextStyle | The editable phone number field |
Custom row renderer
Replace the default country row in the modal list:
<CountryPickerInput
displayMode="dialCode"
value={country}
onSelect={setCountry}
renderCountryRow={(country, onSelect) => (
<Pressable onPress={() => onSelect(country)} style={myRowStyle}>
<CountryFlag isoCode={country.isoCode} size={24} />
<Text>{country.nameEs}</Text>
<Text>{country.dialCode}</Text>
</Pressable>
)}
/>Custom input renderer
Replace the entire trigger input (the tappable field that opens the modal):
<CountryPickerInput
value={country}
onSelect={setCountry}
renderInput={(country, onOpen) => (
<Pressable onPress={onOpen} style={myInputStyle}>
{country && <CountryFlag isoCode={country.isoCode} size={20} />}
<Text>{country?.name ?? 'Select country'}</Text>
</Pressable>
)}
/>selectProps — Customize the dial code selector pressable
Forward extra props to the Pressable that wraps the flag + dial code in dialCode mode. Useful for hitSlop, testID, accessibilityLabel, etc.
<CountryPickerInput
displayMode="dialCode"
value={country}
onSelect={setCountry}
selectProps={{
hitSlop: { top: 14, bottom: 14, left: 14, right: 14 },
accessibilityLabel: 'Open country selector',
testID: 'dial-code-selector',
}}
/>Note:
onPressandstyleare managed internally. Usestyles.dialCodeSelectorto customize the appearance.
Accessing the selected country
The onSelect callback receives the full Country object:
const handleSelect = (country: Country) => {
console.log(country);
// {
// name: "Guatemala",
// nameEs: "Guatemala",
// isoCode: "GT",
// dialCode: "+502",
// currency: "GTQ",
// currencySymbol: "Q"
// }
};API
<CountryPickerInput />
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| value | Country \| null | — | Currently selected country |
| onSelect | (country: Country) => void | — | Called when a country is selected |
| displayMode | CountryDisplayMode | 'name' | Controls what is shown in the input after selection |
| defaultCountry | string | 'GT' | ISO code of the country to pre-select on mount when no value is set |
| inputMode | 'flat' \| 'outlined' | 'outlined' | Visual style — full border or underline only |
| label | string | auto | Label for the text input |
| placeholder | string | auto | Placeholder when no country is selected |
| searchPlaceholder | string | auto | Placeholder for the modal search input |
| modalTitle | string | auto | Title at the top of the picker modal |
| language | 'en' \| 'es' | 'en' | Language for UI strings and country names |
| showDialCode | boolean | false | Show the dial code alongside the name in the modal list |
| preferredCountries | string[] | [] | ISO codes of countries pinned to the top of the list |
| excludedCountries | string[] | [] | ISO codes of countries to hide from the list |
| searchable | boolean | true | Show or hide the search input in the modal |
| disabled | boolean | false | Disables the picker |
| error | boolean | false | Puts the input in an error state |
| helperText | string | — | Helper text displayed below the input |
| phoneNumber | string | — | Controlled value for the phone number field (dialCode mode) |
| onChangePhoneNumber | (phone: string) => void | — | Callback when phone number changes (dialCode mode) |
| phonePlaceholder | string | auto | Placeholder for the phone number field |
| phoneInputProps | TextInputProps (partial) | — | Extra props for the phone TextInput |
| styles | CountryPickerStyles | — | Style overrides per section |
| selectProps | PressableProps (partial) | — | Extra props for the dial code selector Pressable |
| renderCountryRow | (country, onSelect) => ReactNode | — | Custom renderer for each modal row |
| renderInput | (country, onOpen) => ReactNode | — | Custom renderer for the input trigger |
CountryDisplayMode
Controls what is displayed inside the input after a country is selected.
type CountryDisplayMode = 'name' | 'dialCode' | 'currency';| Value | Input shows | Auto label (EN) | Auto label (ES) |
|-------|-------------|-----------------|--------------------|
| 'name' | 🇬🇹 Guatemala | Country | País |
| 'dialCode' | 🇬🇹 +502 [editable field] | Phone prefix | Prefijo telefónico |
| 'currency' | 🇬🇹 Q GTQ | Currency | Moneda |
Country type
interface Country {
/** Country name in English (e.g. "Mexico") */
name: string;
/** Country name in Spanish (e.g. "México") */
nameEs: string;
/** ISO 3166-1 alpha-2 code (e.g. "MX") */
isoCode: string;
/** International dial code (e.g. "+52") */
dialCode: string;
/** ISO 4217 currency code (e.g. "MXN") */
currency: string;
/** Currency symbol (e.g. "$") */
currencySymbol: string;
}COUNTRIES
You can import the full countries array for custom filtering or advanced use cases:
import { COUNTRIES } from 'react-native-paper-country-picker';
// Filter to Latin American countries
const latam = COUNTRIES.filter(c =>
['MX', 'GT', 'SV', 'HN', 'NI', 'CR', 'PA', 'CO', 'VE', 'PE', 'BO', 'CL', 'AR', 'UY', 'PY', 'EC', 'BR'].includes(c.isoCode)
);
// Get all Euro countries
const euroZone = COUNTRIES.filter(c => c.currency === 'EUR');
// Get all countries with USD
const usdCountries = COUNTRIES.filter(c => c.currency === 'USD');Changelog
v1.2.5
- 🚀 Complete refactor of standard select input (
StandardSelectInput): uses pure React Native flexbox layout matching Material 3 specifications. Completely replaces PaperTextInput.Iconcustom component rendering, eliminating Fabric Android text layout measurement crashes (Layout: -1 < 0) once and for all while providing 100% pure white background across all modes.
v1.2.4
- 🐛 Completely remove
pointerEventsprop from wrapper Pressable (fixes Fabric Android layout measurement errorLayout: -1 < 0)
v1.2.3
- 🐛 Explicitly set
width: '100%'on Pressable wrapper to fix Fabric Android text layout measurement (Layout: -1 < 0)
v1.2.2
- 🐛 Fix Android Fabric (New Architecture) crash
java.lang.IllegalArgumentException: Layout: -1 < 0by removing innerpointerEvents="none"wrapper View and usingpointerEvents="box-only"on Pressable
v1.2.1
- 🏳️ Default all inputs (including
flatmode fornameandcurrencymodes) to white background (#FFFFFF) instead of MD3 purple surfaceVariant - 🎨 Add
styles.textInputsupport toCountryPickerStylesto easily override main input styles - 🛡️ Add defensive fallback for invalid
displayModevalues (defaults to'name'instead of throwing error)
v1.2.0
- 🇬🇹 New
defaultCountryprop — pre-select any country on mount (defaults to'GT') - 📞
displayMode="dialCode"now renders an editable phone number field alongside the flag+code selector - 🖊 New
inputModeprop ('flat'|'outlined') — works for all display modes includingdialCode - 🎨 New
stylesprop (CountryPickerStyles) — fine-grained style overrides per section - 🧩 New
renderCountryRowprop — custom renderer for each country row in the modal - 🧩 New
renderInputprop — custom renderer for the input trigger - 🎛 New
selectPropsprop — extraPressableprops for the dial code selector (hitSlop, testID, etc.) - ⬇️ Larger chevron icon for improved visibility
- 🏳️ White background on phone input container in all modes
- 📦
CountryPickerStylestype exported from library entry point
v1.1.0
- ✨ New
displayModeprop:'name'|'dialCode'|'currency' - 💱 Added
currency(ISO 4217) andcurrencySymbolfields to all 195 countries - 🔍 Search now also works on currency codes
- 🏷 Auto label/placeholder defaults per
displayModeandlanguage - 📋 Modal row subtitle adapts to the active
displayMode - 📦 Export
CountryDisplayModetype from library entry point
v1.0.0
- 🎉 Initial release
- 🌍 195 countries with flags
- 🌐 Bilingual support (EN + ES)
- 📞 Dial code support
- ⭐ Preferred and excluded countries
- 🔍 Searchable modal
Contributing
See CONTRIBUTING.md.
License
MIT — see LICENSE.
Made with ❤️ using create-react-native-library
