Headless phone number input component for React

Jul 09, 2020
Headless phone number input component for React

React Headless Phone Input

A headless phone number input component built for usability.

Phone numbers are hard. Users expect to be able to enter phone numbers in the format they're used to. Here's the problem: most people are used to national - or even local phone number formats. If you offload phone number validation to your backend (or an API), resolving the ambiguity becomes difficult or even impossible.

This component helps you build a UI that gracefully guides your users towards unambiguous phone number formats. And you get the result in standard e164 format: ready for use with any telephony service.

Other libraries are generally heavy (phone number rulesets can be big - 99.1% of this library's footprint is due to libphonenumber-js), force you to use their UI, and can't handle copy & paste or edit-in-place. react-headless-phone-input is designed for usability-first, and lets you bring your own input components. In fact, your existing input fields will almost certainly work with no modifications. Plus, it supports optional lazy-loading with progressive enhancement powered by React Suspense.

Built with React Hooks.

View Demo View Github


Install both react-headless-input and [libphonenumber-js]:

npm i --save react-headless-phone-input libphonenumber-js


yarn add react-headless-phone-input libphonenumber-js


  • 100% headless: Bring your own UI. You can use almost any input component you already have
  • Lets users copy & paste phone numbers of any format
  • Typescript support
  • Built-in lazy-loading with progressive enhancement (clocks in at 40KB without lazy-loading)
  • Detects the associated country, enabling international phone input.
  • Lets users copy & paste phone numbers of any format
  • Acts like a normal input: Doesn’t glitch if a user edits in-place or deletes template characters
  • Validates number plausibility
  • External state is standard e164 format


This library is headless: you bring your own UI, but it's almost as easy as using regular inputs.

Here's an example using [tiny-flag-react] to show the flag associated with the number's country:

import TinyFlagReact from "tiny-flag-react"; import PhoneFormatter from "react-headless-phone-input"; // import PhoneFormatter from "react-headless-phone-input/lazy"; RECOMMENDED const [e164, setE164] = useState(""); <PhoneFormatter defaultCountry="US" value={e164} onChange={setE164}> {({ country, impossible, onBlur, onInputChange, inputValue }) => { return ( <> <div style={{ display: "flex", alignItems: "center" }}> <span style={{ fontSize: "24px", }}> {country ? ( <TinyFlagReact country={country} alt={country + " flag"} fallbackImageURL={`[email protected]/img/SVG/${country}.svg`} /> ) : ( <></> )} </span> <input type="tel" value={inputValue} onBlur={onBlur} onChange={(e) => onInputChange(} /> </div> {impossible && ( <div style={{ color: "red" }}>Impossible phone number</div> )} </> ); }} </PhoneFormatter>;


Due to this library's dependence on [libphonenumber-js], it clocks in at [38.7KB minified + gzipped][bundlephobia].
To improve your user's experience, react-headless-phone-component supports lazy loading with React Suspense with
progressive auto-enachement. If your bundler supports dynamic imports and are using a compatible version of React,
just swap react-headless-phone-input for react-headless-phone-input/lazy.

Your UI will render and can be used immediately. Once react-headless-phone-input loads, the component will be
automatically upgraded. No other changes are required.

import PhoneFormatter from "react-headless-phone-input/lazy";