How to Validate a Phone Number in JavaScript (libphonenumber-js)
To validate a phone number in JavaScript, use libphonenumber-js, a port of Google's libphonenumber:
import { isValidPhoneNumber, parsePhoneNumberFromString } from "libphonenumber-js";
isValidPhoneNumber("(415) 555-2671", "US"); // true
isValidPhoneNumber("(212) 123-4567", "US"); // false: exchange can't start with 1
const phone = parsePhoneNumberFromString("(415) 555-2671", "US");
phone?.number; // "+14155552671" (E.164, store this)
phone?.country; // "US"
phone?.getType(); // "FIXED_LINE_OR_MOBILE"
It checks numbers against every country's numbering plan, which a regex can't (see phone number validation regex). Every output above is what version 1.13.15 returned when we ran it.
Install
npm install libphonenumber-js
The default import is small enough for the browser. The library also publishes a libphonenumber-js/max entry point with fuller metadata; its documentation recommends it when you need the most precise line-type detection, at the cost of a larger bundle.
Parsing: always give a default country
A number without + only means something in a specific country. Pass that country as the second argument:
parsePhoneNumberFromString("020 7946 0958", "GB")?.number; // "+442079460958"
parsePhoneNumberFromString("020 7946 0958", "US"); // undefined or invalid
parsePhoneNumberFromString("+44 20 7946 0958")?.number; // "+442079460958"
parsePhoneNumberFromString returns undefined for input it can't parse at all (like "hello"), so use optional chaining.
In a form, get the default country from a country selector or the user's account, not from IP geolocation alone. Travellers and VPNs make IP a poor guess.
Valid vs possible
import { isPossiblePhoneNumber, isValidPhoneNumber } from "libphonenumber-js";
isPossiblePhoneNumber("(555) 123-4567", "US"); // true: right length
isValidPhoneNumber("(555) 123-4567", "US"); // false: 555 isn't an area code
- Possible: the length is right for the country. Use it while the user is still typing.
- Valid: the number is in an allocated range. Use it on submit.
Line type
parsePhoneNumberFromString("+44 7400 123456")?.getType(); // "MOBILE"
parsePhoneNumberFromString("+1 800 275 2273")?.getType(); // "TOLL_FREE"
parsePhoneNumberFromString("+1 415 555 2671")?.getType(); // "FIXED_LINE_OR_MOBILE"
FIXED_LINE_OR_MOBILE is the normal answer for US and Canadian numbers, because North America doesn't separate mobile and landline ranges. If you need to know for sure (for SMS, say), you need a carrier lookup. See mobile vs landline lookup.
Formatting for display
const p = parsePhoneNumberFromString("4155552671", "US");
p?.formatNational(); // "(415) 555-2671"
p?.formatInternational(); // "+1 415 555 2671"
p?.getURI(); // "tel:+14155552671"
And while the user types:
import { AsYouType } from "libphonenumber-js";
new AsYouType("US").input("4155552671"); // "(415) 555-2671"
Store .number (E.164), display whichever format suits. More on why in E.164 format explained.
A React form field
import { useState } from "react";
import { isValidPhoneNumber, parsePhoneNumberFromString, type CountryCode } from "libphonenumber-js";
export function PhoneField({ country }: { country: CountryCode }) {
const [value, setValue] = useState("");
const [touched, setTouched] = useState(false);
const valid = isValidPhoneNumber(value, country);
return (
<label>
Phone
<input
type="tel"
autoComplete="tel"
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={() => setTouched(true)}
aria-invalid={touched && !valid}
/>
{touched && !valid && <span role="alert">Check the number and country.</span>}
<input type="hidden" name="phone" value={parsePhoneNumberFromString(value, country)?.number ?? ""} />
</label>
);
}
Validate on blur, not on every keystroke, so people aren't told they're wrong halfway through typing. Use type="tel" and autoComplete="tel" so mobile keyboards and autofill work.
Validate again on the server
The same library runs in Node, so re-check on the server and store E.164:
import { parsePhoneNumberFromString } from "libphonenumber-js";
export function normalisePhone(raw, country) {
const phone = parsePhoneNumberFromString(String(raw ?? ""), country);
if (!phone?.isValid()) return null;
return phone.number;
}
What validation can't tell you
- Whether the number is in service. Disconnected numbers stay in valid ranges.
- Whether it's the user's. Send a one-time code for that.
- Whether it's a real number or an example.
(212) 555-0100is valid but reserved for fiction. See 555 phone numbers.
Validating a file instead
For a CSV export rather than a form, you don't need code. Upload it on the SimpleVerifier verifier and every row gets phone_status, phone_e164, phone_country and phone_type from the same numbering-plan data, included on paid plans (see pricing). For email fields, see validating email in JavaScript.
Related: validate a phone number in Python · how to check if a phone number is valid
Common questions
What's the best way to validate a phone number in JavaScript?
Use libphonenumber-js, a JavaScript port of Google's libphonenumber. isValidPhoneNumber('(415) 555-2671', 'US') checks the number against the country's numbering plan, and parsePhoneNumberFromString gives you the E.164 form, country and number type.
How do I convert a phone number to E.164 in JavaScript?
Parse it with a default country and read .number: parsePhoneNumberFromString('(415) 555-2671', 'US')?.number returns '+14155552671'. Numbers already starting with + don't need a default country.
Should I validate phone numbers on the client or the server?
Both. Validate on the client for instant feedback, and again on the server before storing, because client-side checks can be bypassed. Store the E.164 form.
Can JavaScript check if a phone number is active?
No library can. Validation confirms a number is in an allocated range for its country. Whether it's in service needs a carrier lookup from a paid API, and whether the right person has it needs a verification code.
Check your list before you send
Real mailbox checks over SMTP, and nothing is sent to the address. Sign in with Google and your first 100 email checks are free, no card. Plans for bigger lists are on the pricing page.