Skip to content

Phone input — Migrating

Before migrating, be clear about what you are giving up: libphonenumber-backed packages do carrier-level validity, and this one does length-based possibility. If you rely on knowing that +1 555 555 5555 is unassignable, keep the metadata — or move that check to the server, where it costs nothing.

react-phone-number-input@rxova/react-phone-inputNotes
value / onChangevalue / onChangeBoth E.164; onChange also gets a details object
defaultCountrydefaultCountrySame, ISO 3166-1 alpha-2
country (forces a country)countryControlled; an explicit +… still wins
countriescountriesOrder is preserved here
international—Inferred: typing + switches modes
countryCallingCodeEditable—Always editable; that is what + is for
labels (a locale bundle)localeA BCP 47 tag; names come from Intl
flagsrenderCountryOr nothing — flags are emoji by default
numberInputPropsclassName / style / placeholderPlus the data-* hooks
isValidPhoneNumber(value)details.possibleWeaker. See above.
formatPhoneNumberformatPhoneExported
import { useState } from "react";
import PhoneInput, { isValidPhoneNumber } from "react-phone-number-input";
import "react-phone-number-input/style.css";
function Signup() {
const [value, setValue] = useState("");
return (
<>
<PhoneInput defaultCountry="GB" value={value} onChange={setValue} />
<button type="submit" disabled={!value || !isValidPhoneNumber(value)}>
Continue
</button>
</>
);
}
import { useState } from "react";
import { PhoneInput } from "@rxova/react-phone-input";
function Signup() {
const [value, setValue] = useState("");
const [possible, setPossible] = useState(false);
return (
<>
<PhoneInput
label="Phone"
defaultCountry="GB"
value={value}
onChange={(next, details) => {
setValue(next);
setPossible(details.possible);
}}
/>
<button type="submit" disabled={!possible}>
Continue
</button>
</>
);
}

You drop libphonenumber-js, country-flag-icons, input-format, classnames, prop-types and the stylesheet import — five runtime dependencies, and 41.1 kB brotli down to 3.9 kB.

Move it to where the metadata is free:

// On the server.
import { isValidPhoneNumber } from "libphonenumber-js";
export function check(e164: string) {
return isValidPhoneNumber(e164);
}

The client stops a typo; the server decides whether the number is real. That split is usually what you wanted anyway, because a client-side check can always be bypassed.

react-phone-input-2 emits the number without a leading + by default and hands you a country object as a second argument, so the value shape changes.

react-phone-input-2@rxova/react-phone-inputNotes
value (no +)value (E.164, with +)Prefix your stored values once
onChange(value, country, e, formatted)onChange(e164, details)details.country is the ISO code
countrydefaultCountryLowercase ISO there, either case here
onlyCountriescountries
preferredCountriescountriesPut them first; order is preserved
enableSearch—The native select does type-ahead
inputPropsname, required, placeholder, …Ordinary props
import { useState } from "react";
import { PhoneInput } from "@rxova/react-phone-input";
function Field() {
const [value, setValue] = useState("");
return (
<PhoneInput
label="Phone"
countries={["GB", "IE", "US"]}
defaultCountry="GB"
value={value}
onChange={setValue}
/>
);
}

You also drop four lodash.* packages and prop-types.

The closest relative — also zero-dependency — but it bundles its own country and name data (8.3 kB brotli against this package’s 3.9 kB) rather than using Intl.

react-international-phone@rxova/react-phone-inputNotes
value / onChange({ phone, country })value / onChange(e164, details)details carries the same information
defaultCountrydefaultCountry
countries (country objects)countries (ISO codes)
forceDialCode—The calling code is always visible in +… mode
disableDialCodePrefill—
usePhoneInputusePhoneInputBoth expose a headless hook
import { PhoneInput } from "@rxova/react-phone-input";
function Field() {
return <PhoneInput label="Phone" defaultCountry="ua" />;
}

If you are currently storing whatever the user typed, this is mostly a gain: you get a canonical E.164 value, a country, and a length check.

import { PhoneInput } from "@rxova/react-phone-input";
// Before
function Before() {
return <input type="tel" name="phone" />;
}
// After — the form still posts under `phone`, now as E.164.
function After() {
return <PhoneInput label="Phone" name="phone" defaultCountry="US" />;
}

Existing rows in your database will be in whatever shape they were saved in. parsePhone can normalise them, given a country to interpret national numbers against:

import { parsePhone } from "@rxova/react-phone-input";
parsePhone("(415) 555-2671", "US").e164; // '+14155552671'
parsePhone("020 7123 4567", "GB").e164; // '+442071234567'