The international phone number input for React Native β country code picker, live formatting, validation, and a searchable country list. Works with Expo and bare React Native on iOS and Android.
Drop-in PhoneInput with a Gorhom bottom sheet, or go headless and plug in your own modal, TrueSheet, or native sheet. Fully typed, themeable, RTL-ready, and localized country names out of the box.
npm install react-native-phone-field @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-transformer-text-input react-native-workletsimport { PhoneInput } from 'react-native-phone-field';
<PhoneInput
value={phone}
onChangePhoneNumber={setPhone}
selectedCountry={country}
onChangeSelectedCountry={setCountry}
defaultCountry="FR"
countryNameLocale="fr"
/>- Why use this library?
- Features
- Quick start
- Package exports
- Controlled state
- Country name localization
- Theming & styling
- Country picker (Gorhom sheet)
- RTL
- Error state
- Custom renders
- Headless mode (custom sheet)
- Utilities
- API reference
- FAQ
- Troubleshooting
- Example app
- Contributing
- License
Building a phone field in React Native usually means wiring together a country picker, calling codes, libphonenumber-js, input masking, keyboard handling, and a bottom sheet. react-native-phone-field ships all of that in one package.
| You need⦠| This library gives you⦠|
|---|---|
| International phone input with country flag | PhoneInput with flag, dial code, and masked national number |
| Searchable country list / country code picker | Built-in sheet with search, 240+ countries |
| Phone number validation & formatting | libphonenumber-js + live masking via react-native-transformer-text-input |
| Custom bottom sheet (Gorhom, TrueSheet, Modal) | Headless mode + reusable CountrySelectorContent |
| Multi-language country names | 25 built-in locales + registerCountryNameLocale() |
| Full UI control | Granular styles, render props, light/dark theme, RTL |
Ideal for sign-up flows, OTP / SMS verification, checkout forms, profile settings, and any screen that collects a mobile number.
- International phone number input β flag, calling code (
+33,+1β¦), and formatted national number - Country picker with search β filter by country name, ISO code, or dial code
- Gorhom bottom sheet included β production-ready picker via @gorhom/bottom-sheet
- Headless architecture β use
react-native-phone-field/headlesswith your own sheet (no Gorhom required) - Live input masking β national format as you type (react-native-transformer-text-input)
- Validation helpers β
isValidPhoneNumber,formatPhoneNumber,getCountryByPhoneNumber - Localized country names β
countryNameLocaleprop + 25 built-in languages via i18n-iso-countries - Light / dark theme, RTL, error state, disabled state
- Fully customizable β colors, layout styles, custom flag/caret/picker renders, sheet backdrop & icons
- TypeScript β strict types for countries, props, and headless adapters
- Expo compatible β example app included
npm install react-native-phone-field @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-transformer-text-input react-native-workletsyarn add react-native-phone-field @gorhom/bottom-sheet react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-transformer-text-input react-native-worklets
countries-list,libphonenumber-js, andi18n-iso-countriesare bundled β no extra install.
Follow the Reanimated and Gesture Handler setup guides if needed.
PhoneInput uses a Gorhom bottom sheet by default. Wrap your app with GorhomPhoneFieldProvider:
import { GorhomPhoneFieldProvider } from 'react-native-phone-field';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaProvider>
<GorhomPhoneFieldProvider>
{/* your screens */}
</GorhomPhoneFieldProvider>
</SafeAreaProvider>
);
}Place
GorhomPhoneFieldProvideroutside anySafeAreaViewso the sheet backdrop covers the full screen.
import { useState } from 'react';
import { PhoneInput } from 'react-native-phone-field';
import type { ICountry } from 'react-native-phone-field';
function SignUpScreen() {
const [phone, setPhone] = useState('');
const [country, setCountry] = useState<ICountry | null>(null);
return (
<PhoneInput
value={phone}
onChangePhoneNumber={setPhone}
selectedCountry={country}
onChangeSelectedCountry={setCountry}
defaultCountry="FR"
countryNameLocale="fr"
placeholder="6 12 34 56 78"
/>
);
}Tap the flag to open the country picker. Done.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PhoneInput (default import) β
β βββ PhoneInputCore input, formatting, flag UI β
β βββ useGorhomCountrySelector bottom sheet picker β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PhoneInput from /headless β
β βββ PhoneInputCore + your own countrySelector β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Import | Use when |
|---|---|
react-native-phone-field |
Default β Gorhom sheet included, fastest setup |
react-native-phone-field/headless |
Your own sheet / modal β no Gorhom install |
react-native-phone-field/country-selector |
Reuse the searchable country list in any sheet |
react-native-phone-field/sheets/gorhom |
Gorhom adapter (useGorhomCountrySelector) directly |
PhoneInput is fully controlled. You own two pieces of state:
| State | Type | Description |
|---|---|---|
phone |
string |
National number (without calling code), formatted as you type |
country |
ICountry | null |
Selected country (cca2, flag, callingCode, name) |
const [phone, setPhone] = useState('');
const [country, setCountry] = useState<ICountry | null>(null);
<PhoneInput
value={phone}
onChangePhoneNumber={setPhone}
selectedCountry={country}
onChangeSelectedCountry={setCountry}
/>interface ICountry {
cca2: string; // "FR", "US" β¦
flag: string; // "π«π·"
callingCode: string; // "+33"
name: string; // "France" (English canonical name)
}Set the initial country on first mount (ISO 3166-1 alpha-2):
<PhoneInput defaultCountry="FR" ... />Type-safe with DefaultCountry (alias of libphonenumber-js CountryCode):
import type { DefaultCountry } from 'react-native-phone-field';
const initial: DefaultCountry = 'FR';If omitted, defaults to US.
Translate country names in the picker with a single prop:
<PhoneInput countryNameLocale="fr" defaultCountry="FR" ... />25 built-in locales (zero config):
en, fr, de, es, it, pt, nl, ar, zh, ja, ko, ru, pl, tr, hi, he, sv, da, fi, cs, uk, vi, th, id
For any other language supported by i18n-iso-countries:
import { registerCountryNameLocale, BUILT_IN_COUNTRY_NAME_LOCALES } from 'react-native-phone-field';
import ro from 'i18n-iso-countries/langs/ro.json';
registerCountryNameLocale('ro', ro);
<PhoneInput countryNameLocale="ro" ... />Headless / custom sheet:
import { CountrySelectorContent } from 'react-native-phone-field/country-selector';
<CountrySelectorContent locale="de" title="Land wΓ€hlen" searchPlaceholder="Suchenβ¦" ... />import { useGorhomCountrySelector } from 'react-native-phone-field/sheets/gorhom';
useGorhomCountrySelector({ locale: 'de', title: 'Land wΓ€hlen' });UI strings (
title,searchPlaceholder,notFoundMessage) are passed as props β wire them to your own i18n library (react-i18next,expo-localization, etc.).
<PhoneInput theme="dark" ... /><PhoneInput
colors={{
text: '#ffffff',
placeholder: '#888888',
border: '#333333',
borderFocused: '#0A84FF',
borderError: '#FF453A',
caretColor: '#0A84FF',
}}
/><PhoneInput
borderRadius={16}
borderWidth={2}
height={52}
shadow={{
color: '#000',
offset: { width: 0, height: 2 },
opacity: 0.1,
radius: 4,
elevation: 3,
}}
/><PhoneInput
phoneInputStyles={{
container: { borderColor: '#e94560' },
callingCode: { color: '#e94560', fontWeight: '700' },
input: { fontSize: 18 },
errorText: { marginTop: 8 },
}}
/>Colors (selectorColors):
selectorColors={{
primary: '#e94560',
text: '#000000',
textSecondary: '#999999',
selectedBackground: '#f0f0f0',
searchBackground: '#f5f5f5',
handleIndicator: '#cccccc',
}}Layout β one flat prop per element:
selectorSearchContainerStyle={styles.searchBar}
selectorSearchInputStyle={styles.searchText}
selectorCountryItemStyle={{ paddingVertical: 14 }}
selectorListContentContainerStyle={{ paddingBottom: 40 }}Custom icons & sheet chrome:
selectorRenderSearchIcon={(color) => <MySearch color={color} />}
selectorRenderCheckIcon={(color) => <MyCheck color={color} />}
selectorRenderClearIcon={(color) => <MyClear color={color} />}
selectorBackdropComponent={CustomBackdrop}
selectorBackgroundComponent={CustomBackground}Props for the default PhoneInput import (Gorhom adapter):
| Prop | Default | Description |
|---|---|---|
selectorColors |
light/dark defaults | Sheet palette |
selectorContainerStyle |
β | Header wrapper of sheet content |
selectorTitleStyle |
β | Title text |
selectorSearchContainerStyle |
β | Search bar row |
selectorSearchInputStyle |
β | Search TextInput |
selectorCountryItemStyle |
β | Each country row |
selectorCountryNameStyle |
β | Country name label |
selectorCountryCallingCodeStyle |
β | Dial code in each row |
selectorListContentContainerStyle |
β | List contentContainerStyle |
selectorTitle |
localized default | Sheet title |
countryNameLocale |
'en' |
Locale for country names ('fr', 'de'β¦) |
selectorLocale |
'en' |
Alias of countryNameLocale |
selectorSnapPoints |
['70%', '100%'] |
Sheet snap points |
selectorBackdropComponent |
built-in | Custom Gorhom backdrop |
selectorBackgroundComponent |
β | Custom sheet background |
selectorRenderSearchIcon |
built-in | Custom search icon |
selectorRenderCheckIcon |
built-in | Selected-row checkmark |
selectorRenderClearIcon |
built-in | Search clear button |
modalSearchInputPlaceholder |
localized default | Search placeholder |
modalNotFoundCountryMessage |
localized default | Empty list message |
For full control over country rows or search bar JSX, use
/headless+CountrySelectorContent.
Custom backdrop:
import { BottomSheetBackdrop } from '@gorhom/bottom-sheet';
import type { BottomSheetBackdropProps } from '@gorhom/bottom-sheet';
const CustomBackdrop = (props: BottomSheetBackdropProps) => (
<BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} opacity={0.7} />
);
<PhoneInput selectorBackdropComponent={CustomBackdrop} ... /><PhoneInput rtl defaultCountry="SA" ... />Reorders flag, caret, divider, and calling code for right-to-left layouts (Arabic, Hebrew, etc.).
<PhoneInput
error
errorMessage="Invalid phone number"
...
/>Pair with isValidPhoneNumber(phone, country.cca2) from the utilities.
Override individual parts without replacing the whole component:
<PhoneInput
renderFlag={(country) => <Text>{country?.flag}</Text>}
renderCallingCode={(country) => <Text>{country?.callingCode}</Text>}
renderCaretIcon={(color) => <MyChevron color={color} />}
showCaret={false}
renderCountryPicker={({ country, onPress, disabled }) => (
<Pressable onPress={onPress} disabled={disabled}>
<Text>{country?.cca2}</Text>
</Pressable>
)}
/>Use headless when you do not want Gorhom as a dependency β e.g. React Native Modal, @lodev09/react-native-true-sheet, or a native bottom sheet.
npm install react-native-phone-field react-native-transformer-text-input react-native-workletsimport { useState } from 'react';
import { Modal } from 'react-native';
import { PhoneInput } from 'react-native-phone-field/headless';
import { CountrySelectorContent } from 'react-native-phone-field/country-selector';
import type { CountrySelectorImplementation, ICountry } from 'react-native-phone-field/headless';
function useModalCountrySelector(): CountrySelectorImplementation {
const [visible, setVisible] = useState(false);
return {
controller: {
present: () => setVisible(true),
dismiss: () => setVisible(false),
},
Portal: ({ selectedCountry, onCountrySelect, theme }) => (
<Modal visible={visible} animationType="slide" onRequestClose={() => setVisible(false)}>
<CountrySelectorContent
selectedCountry={selectedCountry}
theme={theme}
locale="en"
onCountrySelect={(country) => {
onCountrySelect(country);
setVisible(false);
}}
/>
</Modal>
),
};
}const countrySelector = useModalCountrySelector();
<PhoneInput
countrySelector={countrySelector}
value={phone}
onChangePhoneNumber={setPhone}
selectedCountry={country}
onChangeSelectedCountry={setCountry}
/>import { useGorhomCountrySelector } from 'react-native-phone-field/sheets/gorhom';
import { PhoneInput } from 'react-native-phone-field/headless';
const countrySelector = useGorhomCountrySelector({
snapPoints: ['60%', '90%'],
locale: 'en',
});
<PhoneInput countrySelector={countrySelector} ... />Phone number helpers powered by libphonenumber-js:
import {
getAllCountries,
getCountryByCca2,
getCountryByPhoneNumber,
getCountriesByCallingCode,
getCountriesByName,
formatPhoneNumber,
isValidPhoneNumber,
countryCodeToFlag,
} from 'react-native-phone-field';
getCountryByCca2('FR');
// { cca2: 'FR', flag: 'π«π·', callingCode: '+33', name: 'France' }
getCountryByPhoneNumber('+33612345678');
formatPhoneNumber('0612345678', 'FR');
// "06 12 34 56 78"
isValidPhoneNumber('0612345678', 'FR');Includes all core props plus Gorhom selector props (selector*).
| Prop | Type | Required | Description |
|---|---|---|---|
value |
string |
Controlled national number | |
defaultValue |
string |
Parse an international number on mount | |
onChangePhoneNumber |
(phone: string) => void |
β | Called on every change |
selectedCountry |
ICountry | null |
Controlled country | |
onChangeSelectedCountry |
(country: ICountry) => void |
β | Called when country changes |
defaultCountry |
DefaultCountry |
Initial country ("FR", "US"β¦) |
|
theme |
'light' | 'dark' |
Default: 'light' |
|
disabled |
boolean |
Disables input and picker | |
error |
boolean |
Error border state | |
errorMessage |
string |
Shown below input when error |
|
rtl |
boolean |
RTL layout | |
placeholder |
string |
Overrides auto-placeholder | |
countrySelector |
CountrySelectorImplementation |
Override default Gorhom picker |
See TypeScript types PhoneInputProps, PhoneInputColors, PhoneInputStyles for the full list.
Yes. The included example app uses Expo. Install peer dependencies and wrap with GorhomPhoneFieldProvider as shown above.
import { isValidPhoneNumber } from 'react-native-phone-field';
const valid = country && isValidPhoneNumber(phone, country.cca2);Combine the calling code with the national number, or use getCountryByPhoneNumber when parsing user input that includes +.
Yes. Import from react-native-phone-field/headless and provide your own countrySelector. Reuse CountrySelectorContent for the searchable country list.
Set countryNameLocale="fr" (or any built-in / registered locale). Override selectorTitle, modalSearchInputPlaceholder, and modalNotFoundCountryMessage for UI copy.
Yes. Pass rtl on PhoneInput and use countryNameLocale="ar" for Arabic country names.
Fully typed exports for props, countries, headless adapters, and selector styles.
react-native-phone-field focuses on a modular architecture: default Gorhom sheet, headless mode, reusable CountrySelectorContent, built-in localization for country names, live masking, and granular style/render customization β without locking you into one bottom-sheet implementation.
Wrap your app with GorhomPhoneFieldProvider (see Quick start).
Place GorhomPhoneFieldProvider outside SafeAreaView.
Install and configure react-native-gesture-handler and react-native-reanimated per their official docs.
Run yarn prepare in the library repo to generate lib/typescript, or reinstall the package.
Use react-native-phone-field/headless and your own countrySelector (see Headless mode).
Clone and run the demo (Default, Headless Gorhom, TrueSheet, Modal, custom styles):
git clone https://github.com/asadhmv/react-native-phone-field.git
cd react-native-phone-field
yarn install
yarn example ios # or: yarn example androidMIT β see LICENSE.