react-native-phone-field
v0.1.0
Published
International phone number input for React Native with country code picker, search, libphonenumber formatting, Gorhom bottom sheet, headless mode, and localized country names. Expo & TypeScript ready.
Maintainers
Keywords
Readme
react-native-phone-field
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"
/>Table of contents
- 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
Why use this library?
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.
Features
- 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
Quick start
1. Install
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.
2. Wrap your app
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.
3. Add the phone input
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.
Package exports
┌─────────────────────────────────────────────────────────┐
│ 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 |
Controlled state
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}
/>ICountry
interface ICountry {
cca2: string; // "FR", "US" …
flag: string; // "🇫🇷"
callingCode: string; // "+33"
name: string; // "France" (English canonical name)
}defaultCountry
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.
Country name localization
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.).
Theming & styling
Light / dark
<PhoneInput theme="dark" ... />Input colors
<PhoneInput
colors={{
text: '#ffffff',
placeholder: '#888888',
border: '#333333',
borderFocused: '#0A84FF',
borderError: '#FF453A',
caretColor: '#0A84FF',
}}
/>Layout & shadow
<PhoneInput
borderRadius={16}
borderWidth={2}
height={52}
shadow={{
color: '#000',
offset: { width: 0, height: 2 },
opacity: 0.1,
radius: 4,
elevation: 3,
}}
/>Granular styles
<PhoneInput
phoneInputStyles={{
container: { borderColor: '#e94560' },
callingCode: { color: '#e94560', fontWeight: '700' },
input: { fontSize: 18 },
errorText: { marginTop: 8 },
}}
/>Country picker (sheet) styling
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}Country picker (Gorhom sheet)
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} ... />RTL
<PhoneInput rtl defaultCountry="SA" ... />Reorders flag, caret, divider, and calling code for right-to-left layouts (Arabic, Hebrew, etc.).
Error state
<PhoneInput
error
errorMessage="Invalid phone number"
...
/>Pair with isValidPhoneNumber(phone, country.cca2) from the utilities.
Custom renders
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>
)}
/>Headless mode (custom sheet)
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.
Minimal install (no Gorhom)
npm install react-native-phone-field react-native-transformer-text-input react-native-workletsReact Native Modal example
import { 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}
/>Gorhom adapter (headless + Gorhom sheet)
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} ... />Utilities
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');API reference
PhoneInput props (default import)
Includes all core props plus Gorhom selector props (selector*).
Core
| 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.
FAQ
Does this work with Expo?
Yes. The included example app uses Expo. Install peer dependencies and wrap with GorhomPhoneFieldProvider as shown above.
How do I validate a phone number?
import { isValidPhoneNumber } from 'react-native-phone-field';
const valid = country && isValidPhoneNumber(phone, country.cca2);How do I get the full international number (E.164)?
Combine the calling code with the national number, or use getCountryByPhoneNumber when parsing user input that includes +.
Can I use this without @gorhom/bottom-sheet?
Yes. Import from react-native-phone-field/headless and provide your own countrySelector. Reuse CountrySelectorContent for the searchable country list.
How do I change the country picker language?
Set countryNameLocale="fr" (or any built-in / registered locale). Override selectorTitle, modalSearchInputPlaceholder, and modalNotFoundCountryMessage for UI copy.
Does it support RTL / Arabic?
Yes. Pass rtl on PhoneInput and use countryNameLocale="ar" for Arabic country names.
Is it TypeScript-friendly?
Fully typed exports for props, countries, headless adapters, and selector styles.
What is the difference from react-native-phone-number-input or similar packages?
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.
Troubleshooting
Country picker sheet does not open
Wrap your app with GorhomPhoneFieldProvider (see Quick start).
Sheet backdrop does not cover the status bar
Place GorhomPhoneFieldProvider outside SafeAreaView.
GestureHandlerRootView / Reanimated errors
Install and configure react-native-gesture-handler and react-native-reanimated per their official docs.
TypeScript cannot find exports
Run yarn prepare in the library repo to generate lib/typescript, or reinstall the package.
I don't want Gorhom
Use react-native-phone-field/headless and your own countrySelector (see Headless mode).
Example app
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 androidContributing
License
MIT — see LICENSE.
