Skip to content

An elegant React Native library for country-aware phone number input and formatting, with smooth UX through native components.

License

Notifications You must be signed in to change notification settings

gtomitsuka/rn-phone-number-input

Repository files navigation

☎️ rn-phone-number-input

npm version Build platforms MIT License

An elegant React Native library for country-aware phone number input and formatting, delivering smooth UX through native components.
FeaturesInstallationGetting StartedAdvanced UsageReference

ios-demo.mp4

Features

  • International phone number input (E.164 formatting)
  • Phone number masks
  • Searchable country list
  • Built-in number verification
  • Handles auto-complete/copy-paste
  • Light/dark modes & styling options
  • Custom UI components
  • i18n-friendly
  • Supports Fabric (React Native 0.71+)
  • Written in TypeScript

Installation

npm install rn-phone-number-input

or

yarn add rn-phone-number-input

And run cd ios && pod install

Getting Started

To provide maximum flexibility, this library relies on two components and a hook:

  • <CountryPickerModal /> (required): Native picker modal that presents countries and handles user selection.
  • usePhoneNumberInput(options) (required): Hook for managing state, formatting/validating numbers, bridging components & more
  • <CountryAwarePhoneInput />: UI element that toggles country picker modal & handles phone number text input. If you'd like to use a custom UI element, refer to Custom Components.
import {
  CountryAwarePhoneInput,
  usePhoneNumberInput,
  CountryPickerModal
} from 'rn-phone-number-input';

export default function App() {
  const inputManager = usePhoneNumberInput({
    darkMode: true,
    defaultCountry: 'GB', // if not set, defaults to 'US'
  });

  return (
    <View>
      <Text>Phone number:</Text>

      <CountryAwarePhoneInput
        manager={inputManager}
        onEndEditing={() => {
          console.log('Finished inputting number');
          // check if number is valid
          console.log('Is valid: ' + inputManager.isValid());
          // output number in e.164 format (e.g., +12133734253)
          console.log(inputManager.getNumber());
        }}
      />
      {/* CountryPickerModal must be at root level of screen! */}
      <CountryPickerModal manager={inputManager} />
    </View>
  );
}

Advanced Usage

Auto-detect user's country

Lowering friction during onboarding can drastically improve sign-up rates – this library enables you to do so through custom default countries.

To do so, you can combine it with react-native-localize:

import { usePhoneNumberInput } from 'rn-phone-number-input';
import * as RNLocalize from "react-native-localize";

const App = () => {
  const inputManager = usePhoneNumberInput({
    defaultCountry: RNLocalize.getCountry(),
  })

  // ...
}

Custom Components

If you require additional functionality or custom UI, you can replace the CountryAwarePhoneInput component. Use the usePhoneNumberInput() hook to provide logic & interface your component with the modal.

import { InputManager, usePhoneNumberInput } from 'rn-phone-number-input';

const ExampleComponent = (manager: InputManager) => {
  const { state, dispatch, isValid, getNumberInfo } = manager;

  // open modal
  dispatch({ type: 'setHidden', payload: false });

  // close modal
  dispatch({ type: 'setHidden', payload: true });

  // change country – requires both ISO code and tel to avoid ambiguity
  dispatch({ type: 'updateCountry', payload: { tel: '+1', code: 'US' } });

  // process user input, parses number & updates number / formatted text
  // copy-pasted / auto-completed number are handled automatically
  dispatch({ type: 'processInput', payload: '7071001000' });

  // advanced phone number logic through libphonenumber-js
  const phoneNumber = getNumberInfo();
  if (phoneNumber) {
    console.log(phoneNumber.getType()); // e.g., "MOBILE"
    console.log(phoneNumber.getURI()); // e.g., "tel:+12345678900"
  }

  // provide (visual) feedback for valid numbers
  const valid = useMemo(() => isValid(), [state.number]);

  return (
    <View style={state.darkMode ? {/* ... */} : {/* ... */}}>
      {/* e.164-formatted phone number, e.g. '+12345678900' */}
      <Text>{state.number}</Text>
      {/* country calling code, e.g. '+1' */}
      <Text>{state.countryTel}</Text>
      {/* ISO-3166 country code, e.g. 'US' */}
      <Text>{state.countryCode}</Text>
      {/* phone number in user-friendly formatting, e.g. '(234) 567-8900' */}
      <Text>{state.formattedText}</Text>
      {/* direct text input by user, e.g. '2345678900' */}
      <Text>{state.inputText}</Text>
    </View>
  );
}

// Usage
const inputManager = usePhoneNumberInput({ ... });
<ExampleComponent manager={inputManager} />

Internationalization

To localize/customize names on the country list, usePhoneNumberInput() accepts an optional localize(countryCode:string) -> string function. The picker will automatically sort countries according to their localized names.

The done button supports custom text as well, but keep in mind left alignment for RTL languages isn't supported yet (PRs welcome!).

import i18nCountries from 'i18n-iso-countries';
i18nCountries.registerLocale(require('i18n-iso-countries/langs/fr.json'));

const App = () => {
  const inputManager = usePhoneNumberInput({
    defaultCountry: 'FR',
    localize: (code) => i18nCountries.getName(code, 'fr'),
  });

  return (
    <View>
      {/* ... */}
      <CountryPickerModal
        manager={inputManager}
        doneButtonText={"Terminé"}/>
    </View>
  );
}

Reference

usePhoneNumberInput(options): InputManager

Options

  • defaultCountry (string): ISO-3601 code of country shown first by default.
  • darkMode (boolean): color scheme used. Please note that changing this may not immediately re-render native component.
  • customCountries (array): if you'd like to need other countries than default.
    • Elements must implement Country interface: { tel: '+1', name: 'United States', emoji: '🇺🇸', code: 'US'}
  • localize (function): to use custom/localized names, add a function in the (countryCode: string) -> string) format

InputManager

  • getNumber() -> string: E.164-formatted international phone number (ex.: +123456789)
  • getCountry() -> string: ISO-3601 country code selected by user (ex.: US)
  • getCallingCode() -> string: Calling code for country selected by user (ex.: +1)
  • isValid() -> string: Verifies if number is valid.
  • getNumberInfo() -> PhoneNumber: Additional phone number information. See PhoneNumber for reference.
  • state: Offers direct access to state. See Advanced Usage.
  • dispatch({type, payload}): Handles user input. See Advanced Usage.

<CountryAwarePhoneInput />

Props

  • manager (required): uses InputManager for logic
  • onEndEditing: event handler for when user dismisses text input
  • containerStyles: override container styles, refer to this file for defaults.
  • fieldStyles: override input field styles, refer to this file for defaults.
  • textStyles: override text styles, mostly used when special fonts / font sizes are required.

<CountryPickerModal />

Important: Keep this at the root of the view hierarchy for the screen.

Props

  • manager (required): uses InputManager for logic
  • toolbarStyles: custom styles for native toolbar. Avoid usage unless necessary and prefer OS-specific code with Platform.OS. Can lead to compatibility issues.
  • pickerStyles: custom styles for native picker. Avoid usage unless necessary and prefer OS-specific code with Platform.OS. Can lead to compatibility issues.

countries

Array of Country objects. For direct access to the list of countries used in this library. Format: { tel: '+1', name: 'United States', emoji: '🇺🇸', code: 'US'}