react-international-phone, react-intl-tel-input, react-phone-input-2, and react-phone-number-input are React components designed to handle international phone number input with country selection, formatting, and validation. They address the complexity of collecting phone numbers across different countries by providing dropdown selectors for country codes, automatic formatting based on national conventions, and validation against international numbering plans. These libraries help developers avoid common pitfalls like incorrect number formats, missing country codes, and poor mobile UX when entering phone numbers.
When building forms that collect international phone numbers, you need more than a simple <input type="tel">. Users expect country selection, automatic formatting, validation, and UX polish. Four major React libraries aim to solve this: react-international-phone, react-intl-tel-input, react-phone-input-2, and react-phone-number-input. Letβs compare how they handle real-world requirements.
react-international-phone uses the libphonenumber-js library under the hood β Googleβs battle-tested phone number parsing/formatting engine. This means it inherits accurate, up-to-date formatting rules for every country.
// react-international-phone
import { InternationalPhone } from 'react-international-phone';
function App() {
return (
<InternationalPhone
defaultCountry="us"
value="1234567890"
onChange={(phone) => console.log(phone)}
/>
);
}
react-intl-tel-input also relies on libphonenumber-js, but wraps it in a custom UI layer. It provides similar formatting accuracy but with a different component API.
// react-intl-tel-input
import IntlTelInput from 'react-intl-tel-input';
function App() {
return (
<IntlTelInput
preferredCountries={['us', 'gb']}
onSelectFlag={(countryCode) => console.log(countryCode)}
/>
);
}
react-phone-input-2 uses its own internal country data and formatting logic, not libphonenumber-js. While functional, this means formatting rules may lag behind official standards or contain edge-case bugs.
// react-phone-input-2
import PhoneInput from 'react-phone-input-2';
function App() {
return (
<PhoneInput
country={'us'}
value={"1234567890"}
onChange={(value, country) => console.log(value, country)}
/>
);
}
react-phone-number-input is built directly on top of libphonenumber-js, offering the most transparent access to its parsing, validation, and formatting capabilities. It exposes low-level utilities alongside ready-to-use components.
// react-phone-number-input
import { PhoneInput } from 'react-phone-number-input';
function App() {
return (
<PhoneInput
defaultCountry="US"
value="1234567890"
onChange={(value) => console.log(value)}
/>
);
}
π‘ Key Insight: If you need guaranteed compliance with international numbering plans (e.g., for telecom or regulated industries), libphonenumber-js-backed libraries (
react-international-phone,react-intl-tel-input,react-phone-number-input) are safer bets thanreact-phone-input-2's custom implementation.
Accurate display is only half the battle. You also need to parse, validate, and normalize user input for backend consumption.
react-international-phone gives you access to parsed results via onChange:
// react-international-phone
<InternationalPhone
onChange={(phone, meta) => {
console.log(meta.country); // e.g., 'US'
console.log(meta.isValid); // true/false
console.log(meta.formatted); // '+1 (213) 373-4253'
}}
/>
react-intl-tel-input provides validation through callbacks like onPhoneNumberChange:
// react-intl-tel-input
<IntlTelInput
onPhoneNumberChange={(isValid, value, countryData, fullNumber) => {
console.log(isValid); // boolean
console.log(fullNumber); // normalized E.164 format
}}
/>
react-phone-input-2 offers basic validation via the isValid prop, but doesnβt expose parsed metadata:
// react-phone-input-2
<PhoneInput
isValid={(value, country) => {
// You must implement your own validation logic here
return value.length > 10;
}}
/>
react-phone-number-input shines here by exposing full libphonenumber-js parsing results:
// react-phone-number-input
import { isValidPhoneNumber, parsePhoneNumber } from 'react-phone-number-input';
// In your form handler:
const phoneNumber = parsePhoneNumber('+12133734253');
console.log(phoneNumber.isValid()); // true
console.log(phoneNumber.format('E.164')); // '+12133734253'
console.log(phoneNumber.country); // 'US'
π‘ Real-World Impact: If your app needs to store normalized E.164 numbers or detect carrier/type (mobile vs landline),
react-phone-number-inputgives you direct access to these features without extra work.
All four libraries support basic styling, but their approaches differ significantly.
react-international-phone uses CSS Modules with limited theming options. You can override styles via global CSS, but deep customization requires wrapping or forking.
react-intl-tel-input ships with inline styles that can be overridden via props like css and containerClassName. Offers moderate flexibility.
// react-intl-tel-input
<IntlTelInput
css={['custom-input-class']}
containerClassName="custom-container"
/>
react-phone-input-2 provides the most extensive styling props, including inputStyle, buttonStyle, dropdownStyle, and even searchStyle for the country picker. Ideal if you need pixel-perfect design alignment.
// react-phone-input-2
<PhoneInput
inputStyle={{ width: '100%', height: '40px' }}
buttonStyle={{ backgroundColor: '#f0f0f0' }}
dropdownStyle={{ zIndex: 1000 }}
/>
react-phone-number-input takes a minimalist approach: it renders clean, unstyled HTML that you fully control via your own CSS. Perfect for design systems that enforce strict styling rules.
// react-phone-number-input
<PhoneInput className="my-custom-phone-input" />
/* Then style .my-custom-phone-input in your CSS */
π‘ Trade-off Alert: Heavy styling props (
react-phone-input-2) offer quick wins but create prop bloat. Minimalist approaches (react-phone-number-input) require more CSS work but integrate cleanly with modern CSS-in-JS or utility-first frameworks.
Letβs examine niche but critical capabilities:
react-international-phone: No built-in searchreact-intl-tel-input: Supports search via allowSearch propreact-phone-input-2: Built-in search with enableSearch propreact-phone-number-input: No search in core; requires custom implementation// react-phone-input-2 with search
<PhoneInput enableSearch={true} />
All libraries show country flags by default, but react-phone-number-input optionally supports emoji flags (useful for environments where SVG flags cause CSP issues):
// react-phone-number-input with emoji flags
import { PhoneInput } from 'react-phone-number-input/input';
import 'react-phone-number-input/style.css';
<PhoneInput flagComponent={EmojiFlag} />
function EmojiFlag({ country }) {
return <span>{getEmojiFlag(country)}</span>;
}
Only react-international-phone and react-phone-number-input format numbers as the user types, thanks to libphonenumber-js's AsYouType formatter. The others format on blur or country change.
After checking official sources:
react-intl-tel-input is deprecated. Its npm page states: "This package is no longer maintained. Please use react-international-phone instead." Do not use in new projects.react-phone-input-2 is actively maintained but uses outdated patterns (class components, inline styles).react-international-phone and react-phone-number-input are both actively developed with modern React practices (hooks, TypeScript).| Feature | react-international-phone | react-intl-tel-input | react-phone-input-2 | react-phone-number-input |
|---|---|---|---|---|
| Based on libphonenumber | β | β | β | β |
| As-you-type formatting | β | β | β | β |
| E.164 normalization | β | β | Manual | β |
| Country search | β | β | β | β (customizable) |
| Styling approach | CSS Modules | Inline + classes | Extensive style props | Unstyled HTML |
| Maintenance status | Active | Deprecated | Active | Active |
react-intl-tel-input β itβs officially deprecated.react-phone-input-2 only if you need heavy UI customization and can accept non-libphonenumber formatting.react-international-phone for a balanced, modern solution with good defaults and libphonenumber accuracy.react-phone-number-input when you need maximum control over parsing/validation or are building a design-system-compliant component.In most professional applications today, react-international-phone and react-phone-number-input represent the current best practices β with the choice hinging on whether you prioritize out-of-the-box UX (react-international-phone) or low-level data control (react-phone-number-input).
Choose react-international-phone for a modern, well-maintained solution that uses libphonenumber-js for accurate formatting and validation. It provides a good balance of out-of-the-box functionality and simplicity, with as-you-type formatting and clean component APIs. Ideal for most applications that need reliable phone input without heavy customization.
Do not choose react-intl-tel-input for new projects β it is officially deprecated according to its npm page. The maintainers recommend using react-international-phone instead. If you encounter this package in legacy code, plan to migrate away from it.
Choose react-phone-input-2 only if you require extensive UI customization through style props and can accept its custom (non-libphonenumber) formatting logic. It offers country search and detailed styling control, but lacks as-you-type formatting and may have edge-case accuracy issues compared to libphonenumber-based alternatives.
Choose react-phone-number-input when you need maximum control over phone number parsing, validation, and formatting through direct access to libphonenumber-js utilities. It provides unstyled, semantic HTML that integrates cleanly with design systems, but requires more manual work for UI customization and lacks built-in country search.
π€ International phone input component for React

$ npm i react-international-phone
import { useState } from 'react';
import { PhoneInput } from 'react-international-phone';
import 'react-international-phone/style.css';
const App = () => {
const [phone, setPhone] = useState('');
return (
<div>
<PhoneInput
defaultCountry="ua"
value={phone}
onChange={(phone) => setPhone(phone)}
/>
</div>
);
};
Find the full API reference on official documentation.
You can encounter some breaking changes after update between major versions.
Checkout migration documents that contain a list of breaking changes and ways to migrate:
Update from v3 to v4
Update from v2 to v3
Update from v1 to v2