libphonenumber-js
A simpler and smaller rewrite of Google Android's libphonenumber
library in javascript.
See Demo
If you’re trying to build a React component with it, take a look at react-phone-number-input
.
LibPhoneNumber
libphonenumber
is an ultimate phone number formatting and parsing library developed by Google for Android phones. It is written in C++ and Java, and, while it has an official autogenerated javascript port, that port is tightly coupled to Google's closure
javascript framework, and, when compiled into a bundle, weighs about 530 kB (330 kB code + 200 kB metadata).
With many websites today asking for user's phone number, the internet could benefit from a simpler and smaller library that would just get the parsing and formatting right, and that's what libphonenumber-js
is.
Difference from Google's libphonenumber
-
Smaller footprint: 130 kB (55 kB code + 75 kB sufficient metadata) vs the original Google's 530 kB (330 kB code + 200 kB full metadata).
-
Can search for phone numbers in text (Google's autogenerated javascript port can't).
-
Doesn't parse or format special "local"-only phone numbers: numbers specific to a "local" area (for example, a city) with the "area code" omitted (like 456-789
with the "area code" (123)
omitted vs. the full (123) 456-789
number), emergency phone numbers like 911
, "short codes" (short SMS-only numbers), numbers starting with a *
(like *555
), etc.
-
Doesn't provide an equivalent of formatNumberForMobileDialing()
function that returns a number formatted in such a way that it can be dialed from a mobile phone within a specific country. For example, it adds "carrier codes" when dialing within certain countries (Brazil, Colombia). Since this is not a dialing library, it doesn't provide such a function and doesn't use "carrier codes" when formatting phone numbers.
-
Doesn't parse alphabetic phone numbers like 1-800-GOT-MILK
.
-
Doesn't use hyphens and brackets when formatting international phone numbers, only whitespace.
-
Doesn't use the "001"
country code for "non-geographic" phone numbers. Instead, PhoneNumber
class has an .isNonGeographic()
method.
GitHub
On March 9th, 2020, GitHub, Inc. silently banned my account (erasing all my repos, issues and comments) without any notice or explanation. Because of that, all source codes had to be promptly moved to GitLab. GitHub repo is now deprecated, and the latest source codes can be found on GitLab, which is also the place to report any issues.
Install
via npm
$ npm install libphonenumber-js --save
via yarn
$ yarn add libphonenumber-js
If you're not using a bundler then use a standalone version from a CDN.
Use
Parse phone number
import parsePhoneNumber from 'libphonenumber-js'
const phoneNumber = parsePhoneNumber('Phone: 8 (800) 555 35 35.', 'RU')
if (phoneNumber) {
phoneNumber.country === 'RU'
phoneNumber.number === '+78005553535'
phoneNumber.isValid() === true
phoneNumber.getType() === 'TOLL_FREE'
}
Format phone number
import parsePhoneNumber from 'libphonenumber-js'
const phoneNumber = parsePhoneNumber('+12133734253')
phoneNumber.formatInternational() === '+1 213 373 4253'
phoneNumber.formatNational() === '(213) 373-4253'
phoneNumber.getURI() === 'tel:+12133734253'
"As You Type" formatter
import { AsYouType } from 'libphonenumber-js'
new AsYouType().input('+12133734')
new AsYouType('US').input('2133734')
Full-text search
import { findPhoneNumbersInText } from 'libphonenumber-js'
findPhoneNumbersInText(`
For tech support call +7 (800) 555-35-35 internationally
or reach a local US branch at (213) 373-4253 ext. 1234.
`, 'US')
"min" vs "max" vs "mobile" vs "core"
This library provides different "metadata" sets, "metadata" being a list of phone number parsing and formatting rules for all countries. The complete list of those rules is huge, so this library provides a way to optimize bundle size by choosing between max
, min
, mobile
and "custom" metadata:
-
max
— The complete metadata set, is about 140 kilobytes
in size (libphonenumber-js/metadata.full.json
). Choose this when you need the most strict version of isValid()
, or if you need to detect phone number type ("fixed line", "mobile", etc).
-
min
— (default) The smallest metadata set, is about 75 kilobytes
in size (libphonenumber-js/metadata.min.json
). Choose this by default: when you don't need to detect phone number type ("fixed line", "mobile", etc), or when a basic version of isValid()
is enough. The min
metadata set doesn't contain the regular expressions for phone number digits validation (via .isValid()
) and detecting phone number type (via .getType()
) for most countries. In this case, .isValid()
still performs some basic phone number validation (for example, checks phone number length), but it doesn't validate phone number digits themselves the way max
metadata validation does.
-
mobile
— The complete metadata set for dealing with mobile numbers only, is about 95 kilobytes
in size (libphonenumber-js/metadata.mobile.json
). Choose this when you need max
metadata and when you only accept mobile numbers. Other phone number types will still be parseable, but they won't be recognized as being "valid" (.isValid()
will return false
).
To use a particular metadata set, simply import functions from a relevant sub-package:
libphonenumber-js/max
libphonenumber-js/min
libphonenumber-js/mobile
Importing functions directly from libphonenumber-js
effectively results in using the min
metadata.
Sometimes (rarely) not all countries are needed, and in those cases developers may want to generate their own "custom" metadata set. For those cases, there's libphonenumber-js/core
sub-package which doesn't come pre-packaged with any default metadata set and instead accepts metadata as the last argument of each exported function.
Definitions
Country code
"Country code" means either a two-letter ISO country code (like US
).
Non-geographic
There're several calling codes that don't belong to any country:
Such phone numbering plans are called "non-geographic", and their phone numbers have country
set to undefined
.
National (significant) number
"National (significant) number" are the national phone number digits (without "national prefix"). For example, +1 213 373 4253
(or (213) 373-4253
in national format) is a US phone number and its national (significant) number is 213 373 4253
. Another example is +33 1 45 45 32 45
(or 01 45 45 32 45
in national format) which is a French phone number where they add 0
"national prefix" when writing phone numbers in national format; in this case the national (significant) number is 1 45 45 32 45
.
Country calling code
"Country calling code" are the digits between the +
and the national (significant) number when the number is written in international format. E.g. for US country calling code is 1
and for France it's 33
. Several countries can share the same "country calling code", e.g. NANPA countries like USA and Canada sharing the same 1
country calling code.
API
parsePhoneNumberFromString(string, [options or defaultCountry]): PhoneNumber
Parses a phone number from string
.
Can be imported both as a "default" export and as a "named" export.
import parsePhoneNumberFromString from 'libphonenumber-js'
const phoneNumber = parsePhoneNumberFromString('Call: (213) 373-42-53 ext. 1234.', 'US')
if (phoneNumber) {
console.log(phoneNumber.formatNational())
}
Returns an instance of PhoneNumber
class, or undefined
if no phone number could be parsed: for example, when the string contains no phone number, or the phone number starts with a non-existent country calling code, etc.
Available options
:
-
defaultCountry
— Default country for parsing national numbers. Instead of passing options.defaultCountry
one could pass defaultCountry
argument directly.
-
defaultCallingCode
— Default calling code for parsing national numbers. Some numbering plans are for "non-geographic numbering plans" and they don't have a country code, so defaultCountry
can't be specified for them.
If a developer wants to know the exact reason why the phone number couldn't be parsed then they can use parsePhoneNumberWithError()
function which throws the exact error:
import { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js'
try {
const phoneNumber = parsePhoneNumberWithError('Call: (213) 373-42-53 ext. 1234.', 'US')
} catch (error) {
if (error instanceof ParseError) {
console.log(error.message)
} else {
throw error
}
}
Possible errors
-
INVALID_COUNTRY
— When defaultCountry
doesn't exist (parsePhoneNumber('(111) 222-3333', 'XX')
) or isn't supported by this library, or when parsing non-international number without a defaultCountry
(parsePhoneNumber('(111) 222-3333')
), or when international number country calling code doesn't exist (parsePhoneNumber('+9991112223333')
).
-
NOT_A_NUMBER
— When no phone number was found. For example, when there are no digits ("abcde"
) or when there's not enough digits (parsePhoneNumber('2', 'US')
, parsePhoneNumber('+1')
).
-
TOO_LONG
— When national (significant) number is too long (17 digits max) or when the string being parsed is too long (250 characters max).
-
TOO_SHORT
— When national (significant) number is too short (for ex. 1 digit).
PhoneNumber
PhoneNumber
class instance has the following properties:
number: string
— The phone number in E.164
format. Example: "+12133734253"
.countryCallingCode: string
— The country calling code. Example: "1"
.nationalNumber: string
— The national (significant) number. Example: "2133734253"
.country: string?
— The country code. Example: "US"
. Will be undefined
when no country
could be derived from the phone number. For example, when several countries have the same countryCallingCode
and the nationalNumber
doesn't look like it belongs to any of them. Or when a number belongs to a non-geographic numbering plan.ext: string?
— The phone number extension, if any. Example: "1234"
.carrierCode: string?
— The "carrier code", if any. Example: "15"
. "Carrier codes" are only used in Colombia and Brazil and only when dialing within those countries from a mobile phone to a fixed line number.
PhoneNumber
class instance provides the following methods:
format(format: string, [options]): string
Formats the phone number into a string according to a format
.
Available format
s:
NATIONAL
— Example: "(213) 373-4253"
INTERNATIONAL
— Example: "+1 213 373 4253"
E.164
— Example: "+12133734253"
RFC3966
(the phone number URI) — Example: "tel:+12133734253;ext=123"
IDD
— "Out-of-country" dialing format. Example: "011 7 800 555 35 35"
for +7 800 555 35 35
being called out of options.fromCountry === "US"
. If no options.fromCountry
was passed or if there's no default IDD prefix for options.fromCountry
then returns undefined
.
Available options
:
-
formatExtension(number, extension)
— Formats number
and extension
into a string. By default returns ${number} ext. ${extension}
for almost all countries with rare exceptions of some special cases like ${number} x${extension}
for UK.
-
nationalPrefix: Boolean
— Some phone numbers can be formatted both with national prefix and without it. In such cases the library defaults to "with national prefix" (for legacy reasons). Pass nationalPrefix: false
option to force formatting without national prefix in such cases.
Examples:
import parsePhoneNumber from 'libphonenumber-js'
const phoneNumber = parsePhoneNumber('+12133734253')
phoneNumber.format("NATIONAL") === '(213) 373-4253'
phoneNumber.format("INTERNATIONAL") === '+1 213 373 4253'
phoneNumber.format("RFC3966") === 'tel:+12133734253'
phoneNumber.formatNational() === phoneNumber.format("NATIONAL")
phoneNumber.formatInternational() === phoneNumber.format("INTERNATIONAL")
phoneNumber.getURI() === phoneNumber.format("RFC3966")
isPossible(): boolean
Checks if the phone number is "possible". Only checks the phone number length, doesn't check the number digits against any regular expressions.
isValid(): boolean
Checks if the phone number is "valid". First checks the phone number length and then checks the phone number digits against all available regular expressions.
By default the library uses "minimal" metadata which is only 75 kilobytes in size but also doesn't include the precise validation regular expressions resulting in less strict validation rules (some very basic validation like length check is still included for each country). If you don't mind the extra 65 kilobytes of metadata then use "full" metadata instead (140 kilobytes). Google's library always uses "full" metadata so it will yield different isValidNumber()
results compared to the "minimal" metadata used by default in this library.
See an example illustrating different results when using /min
vs /max
vs /mobile
import parseMin from 'libphonenumber-js/min'
import parseMax from 'libphonenumber-js/max'
import parseMobile from 'libphonenumber-js/mobile'
parseMin('+6589555555').isValid() === true
parseMax('+6589555555').isValid() === false
parseMobile('+6589555555').isValid() === false
See also "Using phone number validation feature" considerations.
getType(): string?
Returns phone number type (fixed line, mobile, toll free, etc) or undefined
(if the number is invalid or if there are no phone number type regular expressions for this country in metadata).
By default the library uses "minimal" metadata which is only 75 kilobytes in size but also doesn't include the regular expressions for determining a specific phone number type (fixed line, mobile, toll free, etc) resulting in getType()
returning undefined
for most countries. If you don't mind the extra 65 kilobytes of metadata then use "full" metadata instead (140 kilobytes). Google's library always uses "full" metadata so it will yield different getNumberType()
results compared to the "minimal" metadata used by default in this library.
The list of possible return values
MOBILE
FIXED_LINE
FIXED_LINE_OR_MOBILE
PREMIUM_RATE
TOLL_FREE
SHARED_COST
VOIP
PERSONAL_NUMBER
PAGER
UAN
VOICEMAIL
See an example illustrating different results when using /min
vs /max
vs /mobile
import parseMin from 'libphonenumber-js/min'
import parseMax from 'libphonenumber-js/max'
import parseMobile from 'libphonenumber-js/mobile'
parseMin('+6584655555').getType() === undefined
parseMax('+6584655555').getType() === 'MOBILE'
parseMobile('+6584655555').getType() === 'MOBILE'
isNonGeographic(): boolean
Returns true
if the number belongs to a "non-geographic numbering plan".
isEqual(phoneNumber: PhoneNumber): boolean
Compares two PhoneNumber
s: returns true
if they're equal, false
otherwise.
class
AsYouType([options or defaultCountry])
Creates a formatter for a partially entered phone number.
Available options
:
-
defaultCountry
— Default country for parsing national numbers. Instead of passing options.defaultCountry
one could pass defaultCountry
argument directly.
-
defaultCallingCode
— Default calling code for parsing national numbers. Some numbering plans are for "non-geographic numbering plans" and they don't have a country code, so defaultCountry
can't be specified for them.
The formatter instance has the following methods:
new AsYouType().input('+12133734') === '+1 213 373 4'
new AsYouType('US').input('2133734') === '(213) 373-4'
The formatter instance also provides the following getters:
-
getNumber(): PhoneNumber
— Returns the PhoneNumber
. Will return undefined
if no national (significant) number digits have been entered so far, or if no defaultCountry
/defaultCallingCode
has been set and the user enters a phone number not in international format.
-
getTemplate(): string
— Returns the template used to format the output. Digits (and the +
sign, if present) are denoted by x
-es.
const asYouType = new AsYouType('US')
asYouType.input('2') === '2'
asYouType.getNumber().number === '+12'
asYouType.getTemplate() === 'x'
asYouType.input('1') === '21'
asYouType.getNumber().number === '+121'
asYouType.getTemplate() === 'xx'
asYouType.input('3') === '(213)'
asYouType.getNumber().number === '+1213'
asYouType.getTemplate() === '(xxx)'
asYouType.input('3734253') === '(213) 373-4253'
asYouType.getNumber().number === '+12133734253'
asYouType.getTemplate() === '(xxx) xxx-xxxx'
const asYouType = new AsYouType()
asYouType.input('+1-213-373-4253') === '+1 213 373 4253'
asYouType.getNumber().country === 'US'
asYouType.getNumber().number === '+12133734253'
asYouType.getTemplate() === 'xx xxx xxx xxxx'
-
isInternational(): boolean
— Returns true
if the phone number is being input in international format. In other words, returns true
if and only if the parsed phone number starts with a "+"
.
-
getCountryCallingCode(): string
— Returns the "country calling code" part of the phone number. Returns undefined
if the number is not being input in international format. Returns "country calling code" for "non-geographic" phone numbering plans too.
-
getCountry(): string
— Returns a two-letter country code of the phone number. Returns undefined
for "non-geographic" phone numbering plans. Returns undefined
if no phone number has been input yet.
-
isPossible(): boolean
— Returns true
if the phone number is "possible". Is just a shortcut for PhoneNumber.isPossible()
.
-
isValid(): boolean
— Returns true
if the phone number is "valid". Is just a shortcut for PhoneNumber.isValid()
.
Legacy API (before version 1.6.0
)
For legacy API (before version 1.6.0
) the formatter instance provides the following getters:
-
country: string?
— Phone number country. Will return undefined
if the country couldn't be derived from the number.
-
getNationalNumber(): string
— Returns the national (significant) number part of the phone number.
-
getTemplate(): string?
— Returns the template used to format the output. Digits (and the +
sign, if present) are denoted by x
-es. Will return undefined
if no suitable format was found for the number being entered (or if no national (significant) number has been entered so far).
const asYouType = new AsYouType('US')
asYouType.input('2') === '2'
asYouType.getNationalNumber() === '2'
asYouType.getTemplate() === 'x'
asYouType.input('1') === '21'
asYouType.getNationalNumber() === '21'
asYouType.getTemplate() === 'xx'
asYouType.input('3') === '(213)'
asYouType.getNationalNumber() === '213'
asYouType.getTemplate() === '(xxx)'
asYouType.input('3734253') === '(213) 373-4253'
asYouType.getNationalNumber() === '2133734253'
asYouType.getTemplate() === '(xxx) xxx-xxxx'
const asYouType = new AsYouType()
asYouType.input('+1-213-373-4253') === '+1 213 373 4253'
asYouType.country === 'US'
asYouType.getNationalNumber() === '2133734253'
asYouType.getTemplate() === 'xx xxx xxx xxxx'
"As You Type" formatter was created by Google as part of their Android OS and therefore only works for numerical keyboard input, i.e. it can only accept digits (and a +
sign in the start of an international number). When used on desktops where a user can input all kinds of punctuation (spaces, dashes, parens, etc) it simply ignores everything except digits (and a +
sign in the start of an international number).
Google's "As You Type" formatter does not support entering phone number extensions. If your project requires phone number extensions input then use a separate input field for that.
findPhoneNumbersInText(text: string, [defaultCountry: string?], [options]): object[]
Searches for phone numbers in text
.
Available options
:
-
defaultCountry
— Default country for parsing national numbers. Instead of passing options.defaultCountry
one could pass defaultCountry
argument directly.
-
defaultCallingCode
— Default calling code for parsing national numbers. Some numbering plans are for "non-geographic numbering plans" and they don't have a country code, so defaultCountry
can't be specified for them.
import { findPhoneNumbersInText } from 'libphonenumber-js'
findPhoneNumbersInText(`
For tech support call +7 (800) 555-35-35 internationally
or reach a local US branch at (213) 373-4253 ext. 1234.
`, 'US')
(in previous versions, it was called findNumbers()
)
Legacy API (before version 1.6.0
) example
import { findNumbers } from 'libphonenumber-js'
findNumbers(`
For tech support call +7 (800) 555-35-35 internationally
or reach a local US branch at (213) 373-4253 ext. 1234.
`, 'US')
By default it processes the whole text and then outputs the phone numbers found. If the text is very big (say, a hundred thousand characters) then it might freeze the user interface for a couple of seconds. To avoid such lags one can employ "iterator" approach using searchPhoneNumbersInText()
to perform the search asynchronously (e.g. using requestIdleCallback
or requestAnimationFrame
).
(in previous versions, it was called searchNumbers()
)
Asynchronous search example using searchPhoneNumbersInText()
ES6 iterator:
import { searchPhoneNumbersInText } from 'libphonenumber-js'
const text = `
For tech support call +7 (800) 555-35-35 internationally
or reach a local US branch at (213) 373-4253 ext. 1234.
`
async function() {
for (const number of searchPhoneNumbersInText(text, 'US')) {
console.log(number)
await new Promise(resolve => setTimeout(resolve, 0))
}
console.log('Finished')
}
Java-style iterator (for those still not using ES6):
import { PhoneNumberMatcher } from 'libphonenumber-js'
const matcher = new PhoneNumberMatcher(`
For tech support call +7 (800) 555-35-35 internationally
or reach a local US branch at (213) 373-4253 ext. 1234.
`, {
defaultCountry: 'US',
v2: true
})
const iteration = () => {
if (matcher.hasNext()) {
console.log(matcher.next())
setTimeout(iteration, 0)
} else {
console.log('Finished')
}
}
iteration()
Although Google's javascript port doesn't have the findPhoneNumbersInText()
functionality the Java and C++ ports do. I guess Google just doesn't need to crawl phone numbers on Node.js because they can afford to hire a Java/C++ developer to do that. Still, javascript nowadays is the most popular programming language given its simplicity and user-friendliness. The findPhoneNumbersInText()
function provided is a port of Google's PhoneNumberMatcher.java
into javascript.
getExampleNumber(country: string, examples: object): PhoneNumber
Returns an example phone number for a country. Returns an instance of PhoneNumber
class. Will return undefined
if country
doesn't exist or isn't supported by this library.
import examples from 'libphonenumber-js/examples.mobile.json'
import { getExampleNumber } from 'libphonenumber-js'
const phoneNumber = getExampleNumber('RU', examples)
phoneNumber.formatNational() === '8 (912) 345-67-89'
isSupportedCountry(country: string): boolean
Checks if a country is supported by this library.
isSupportedCountry('RU') === true
isSupportedCountry('XX') === false
getCountries(): string[]
Returns a list of supported countries.
getCountries() === ["AC", "AD", ...]
getCountryCallingCode(country: string): string
Returns country calling code for a country. Will throw an error if country
doesn't exist or isn't supported by this library.
getCountryCallingCode('RU') === '7'
getCountryCallingCode('IL') === '972'
getExtPrefix(country: string): string
Returns phone number extension prefix for a given country
. If no custom ext prefix is defined for a country
then the default " ext. "
prefix is returned.
getExtPrefix('US') === ' ext. '
getExtPrefix('GB') === ' x'
parseDigits(text: string): string
Parses digits from string. Can be used for building a phone number extension input component (e.g. react-phone-number-input).
parseDigits('x123') === '123'
parseDigits('٤٤٢٣') === '4423'
parseIncompletePhoneNumber(text: string): string
Parses phone number characters (+
and digits). Can be used for building a phone number input component (e.g. react-phone-number-input).
parseIncompletePhoneNumber('8 (800) 555') === '8800555'
parseIncompletePhoneNumber('+7 800 555') === '+7800555'
parseIncompletePhoneNumber('+٤٤٢٣٢٣٢٣٤') === '+442323234'
formatIncompletePhoneNumber(value: string, country: string?): string
Formats incomplete phone number as a national one for a given country
. If country
is not specified then outputs the phone number in international format. This is just an alias for new AsYouType(country, metadata).input(value)
. Can be used for building a phone number input component (e.g. react-phone-number-input).
formatIncompletePhoneNumber('8800555', 'RU') === '8 (800) 555'
formatIncompletePhoneNumber('+7800555') === '+7 800 555'
Legacy API
Legacy API (before version 1.6.0
): parse()
, parseNumber()
, format()
, formatNumber()
, isValidNumber()
, getNumberType()
.
parseNumber(text, [defaultCountry], [options])
(previously called parse()
)
(legacy API)
Attempts to parse a phone number from text
.
If defaultCountry
is passed then it's gonna be the default country for parsing non-international phone numbers.
Returns { country, phone, ext }
object where
If the phone number supplied isn't valid then an empty object {}
is returned.
Examples
parseNumber('+1 213 373 4253') === { country: 'US', phone: '2133734253' }
parseNumber('Phone: +1-213-373-4253.') === { country: 'US', phone: '2133734253' }
parseNumber('+12133734253') === { country: 'US', phone: '2133734253' }
parseNumber('Phone: (213) 373-4253.', 'US') === { country: 'US', phone: '2133734253' }
parseNumber('(213) 373-4253 ext. 123', 'US') === { country: 'US', phone: '2133734253', ext: '123' }
parseNumber('tel:+78005553535;ext=123') === { country: 'RU', phone: '8005553535', ext: '123' }
If the phone number supplied isn't valid then an empty object {}
is returned.
parseNumber('+1 111 111 1111') === {}
parseNumber('(111) 111-1111', 'US') === {}
parseNumber('abcdefg') === {}
Available options
:
-
defaultCountry : string
— Same as the defaultCountry
argument.
-
extended : boolean
— If set to true
then parseNumber()
will attempt to parse even a remotely hypothetical phone number even if it is considered "invalid".
{ extended: true }
documentation and examples
The result of "extended" parsing is an object where
country
is a country code.phone
is a national (significant) number.ext
is a phone number extension.countryCallingCode
is a country calling code.carrierCode
s are only used in Colombia and Brazil and only when dialing within those countries from a mobile phone to a fixed line number.valid: boolean
— whether it's a "valid" (real) phone number.possible: boolean
— a phone number is considered "possible" when it fits the phone number length rules for a given country. E.g. for US national (significant) number regexp is [2-9]\d{9}
and possible national (significant) number length is 10
so a phone number (111) 111-1111
is not a "valid" number because it doesn't match the US national (significant) number regexp but it is a "possible" number because it's 10
digits long.- Some or all of these properties may be absent from the result object.
parseNumber('Phone: (213) 373-4253.', 'US', { extended: true }) ===
{
country: 'US',
phone: '2133734253',
ext: undefined,
countryCallingCode: 1,
carrierCode: undefined,
valid: true,
possible: true
}
parseNumber('(111) 111-1111', 'US', { extended: true }) ===
{
country: 'US',
phone: '1111111111',
ext: undefined,
countryCallingCode: 1,
carrierCode: undefined,
valid: false,
possible: true
}
parseNumber('+1 111 111 1111', { extended: true }) ===
{
country: undefined,
phone: '1111111111',
ext: undefined,
countryCallingCode: 1,
carrierCode: undefined,
valid: false,
possible: true
}
parseNumber('(213) 373', 'US', { extended: true }) ===
{
country: 'US',
phone: '213373',
ext: undefined,
countryCallingCode: 1,
carrierCode: undefined,
valid: false,
possible: false
}
parseNumber('1', 'US', { extended: true }) === {}
parseNumber('+210', { extended: true }) === {}
parseNumber('abcdefg', 'US', { extended: true }) === {}
The "extended" parsing mode is the default behaviour of the original Google's libphonenumber
: it still returns parsed data even if the phone number being parsed is not considered valid (but is kinda "possible"). I guess this kind of behaviour is better for crawling websites for phone numbers because when mining "big data" it is better to extract all possible info rather than discard some pieces of it prematurely, e.g. when national (significant) number regexp for some country gets outdated which might very well happen because phone numbering plans are changing constantly around the world. Maybe after all it would make sense to make the "extended" parsing mode the default one in the next major version. I guess it would.
Also parses IDD-prefixed phone numbers
Sometimes users icorrectly input phone numbers in "out-of-country" dialing (IDD-prefixed) format instead of the proper international phone number format (the "+" notation). In such cases parseNumber()
will attempt to parse such IDD-prefixed numbers if "default country" is provided:
parseNumber('+61 2 3456 7890') === { country: 'AU', phone: '234567890' }
parseNumber('011 61 2 3456 7890', 'US') === { country: 'AU', phone: '234567890' }
formatNumber(number, format, [options])
(previously called format()
)
(legacy API)
Formats a number
into a string according to a format
.
Available format
s and options
are the same as for PhoneNumber.format(format)
.
The number
argument must be either a result of parseNumber()
function call (to strip national prefix) or an E.164 phone number string (e.g. +12133734253
).
Examples
formatNumber('+12133734253', 'NATIONAL') === '(213) 373-4253'
formatNumber('+12133734253', 'INTERNATIONAL') === '+1 213 373 4253'
formatNumber('+11111111111', 'NATIONAL') === '(111) 111-1111'
formatNumber('+11111111111', 'INTERNATIONAL') === '+1 111 111 1111'
formatNumber('+11111', 'NATIONAL') === '1111'
formatNumber('+11111', 'INTERNATIONAL') === '+1 1111'
const parsedNumber = parseNumber('2133734253', 'US')
formatNumber(parsedNumber, 'NATIONAL') === '(213) 373-4253'
formatNumber(parsedNumber, 'INTERNATIONAL') === '+1 213 373 4253'
const possibleNumber = parseNumber('+11111111111', { extended: true })
formatNumber(possibleNumber, 'NATIONAL') === '(111) 111-1111'
formatNumber(possibleNumber, 'INTERNATIONAL') === '+1 111 111 1111'
const possibleNumber = parseNumber('+11111', { extended: true })
formatNumber(possibleNumber, 'NATIONAL') === '1111'
formatNumber(possibleNumber, 'INTERNATIONAL') === '+1 1111'
formatNumber({ country: 'US', phone: '2133734253', ext: '123' }, 'NATIONAL') === '(213) 373-4253 ext. 123'
formatNumber({}) throws Error
getNumberType(number, [defaultCountry])
(legacy API)
See the description for PhoneNumber.getType()
.
The number
argument can be either a result of the parseNumber()
function call — { country, phone }
— or a string (phone number digits only) possibly accompanied with the second defaultCountry
argument.
Examples
getNumberType('+79160151539') === 'MOBILE'
getNumberType('9160151539', 'RU') === 'MOBILE'
getNumberType({ phone: '9160151539', country: 'RU' }) === 'MOBILE'
isValidNumber(number, [defaultCountry])
(legacy API)
Checks if a phone number is valid, the validation is more strict than parseNumber()
.
The number
argument can be either a result of the parseNumber()
function call — { country, phone }
— or a string (phone number digits only) possibly accompanied with the second defaultCountry
argument.
Examples
isValidNumber('+12133734253') === true
isValidNumber('+1213373') === false
isValidNumber('2133734253', 'US') === true
isValidNumber('21337', 'US') === false
isValidNumber({ phone: '2133734253', country: 'US' }) === true
The difference between using parseNumber()
and isValidNumber()
The difference between using parseNumber()
and isValidNumber()
for phone number validation is that isValidNumber()
also checks the precise regular expressions of possible phone numbers for a country. For example, for Germany parseNumber('123456', 'DE')
would return { country: 'DE', phone: '123456' }
because this phone number matches the general phone number rules for Germany (basic length check, etc). But, if the metadata is compiled with --extended
(or relevant --types
) flag (see Customizing metadata section of this document) then isValidNumber()
is gonna use those precise regular expressions for extensive validation and isValid('123456', 'DE')
will return false
because the phone number 123456
doesn't actually exist in Germany.
This is how it is implemented in the original Google's libphonenumber
: parseNumber()
parses phone numbers and loosely validates them while isValidNumber()
validates phone numbers precisely (provided the precise regular expressions are included in metadata).
The precise regular expressions aren't included in the default metadata because that would cause the default metadata to grow twice in its size: the complete ("full") metadata size is about 145 kilobytes while the reduced ("default") metadata size is about 77 kilobytes. Hence in the default configuration isValidNumber()
performs absolutely the same "lite" validation as parseNumber()
. For enabling extensive phone number validation the simplest way is to import functions from libphonenumber-js/custom
module and supply them with libphonenumber-js/metadata.full.json
. For generating custom metadata see the instructions provided in the Customizing metadata section of this document.
isValidNumberForRegion()
The optional defaultCountry
argument is the default country, i.e. it does not restrict to just that country, e.g. in those cases where several countries share the same phone numbering rules (NANPA, Britain, etc). For example, even though the number 07624 369230
belongs to the Isle of Man ("IM" country code) calling isValidNumber('07624369230', 'GB')
still returns true
because the country is not restricted to GB
, it's just that GB
is the default one for the phone numbering rules. For restricting the country see isValidNumberForRegion()
though restricting a country might not be a good idea.
isValidNumber('07624369230', 'GB') === true
isValidNumber('07624369230', 'IM') === true
isValidNumberForRegion('07624369230', 'GB') === false
isValidNumberForRegion('07624369230', 'IM') === true
Using phone number validation feature
I personally don't use strict phone number validation in my projects because telephone numbering plans sometimes change and so validation rules can change too which means that PhoneNumber.isValid()
function may become outdated if a website isn't re-deployed regularly. For example:
- New phone number rules can be added to Google's
libphonenumber
library after they have already been implemented in real life (which can introduce a delay).
-
After that those new rules from Google's libphonenumber
are merged in this library (another delay).
-
And then there's still the web application itself using this library and until a developer installs libphonenumber-js@latest
manually and rebuilds and redeploys the web application, it's gonna use the old (potentially outdated) phone number validation rules which could result in losing rare customers with newly valid (but previously non-valid) phone numbers if a website form is too strict about validating user's input.
Phone number validation rules are being updated regularly in Google's repo. Imagine a web application (for example, a "promo-site" or a "personal website") with a "Contact Us" form being deployed once and then running for years without any maintenance.
If it was required to validate a phone number being input by a user, then I'd personally use something like PhoneNumber.isPossible()
that just validates phone number length.
React
If you’re trying to build a React component with this library, take a look at react-phone-number-input
.
Bug reporting
When reporting an issue one must also provide a link to Google's libphonenumber
demo page illustrating the expected behaviour. This includes validation, parsing, formatting and "as you type" formatting. For example, for an Australian number 438 331 999
Google's demo outputs four sections — "Parsing Result", "Validation Results", "Formatting Results" and "AsYouTypeFormatter Results". In a bug report, first describe the observed libphonenumber-js
demo result and then Google's demo result (with a link to it) which must differ from the observed libphonenumber-js
demo result. If the observed libphonenumber-js
demo result is the same as Google's demo result and you don't agree with Google's demo result then create an issue in Google's repo.
When reporting findPhoneNumbersInText()
bugs one should know that findPhoneNumbersInText()
code was ported from Google's Java code. I didn't write it myself, I just ported it. Therefore, it is unlikely that anyone other than Google will be fixing such bugs.
TypeScript
TypeScript support for this library is entirely community-driven. I myself don't use TypeScript. Send your pull requests.
CDN
One can use any npm CDN service, e.g. unpkg.com or jsdelivr.com
<script src="https://unpkg.com/libphonenumber-js@[version]/bundle/libphonenumber-[type].js"></script>
<script>
alert(new libphonenumber.AsYouType('US').input('213-373-4253'))
</script>
where [version]
is an npm package version range (for example, 1.x
or ^1.7.6
) and [type]
is the bundle type: min
, max
or mobile
.
Metadata
Metadata is generated from Google's PhoneNumberMetadata.xml
by transforming XML into JSON and removing unnecessary fields. See metadata fields description.
Programmatic access
Metadata can be accessed programmatically by using the exported Metadata
class.
import { Metadata } from 'libphonenumber-js/core'
import minMetadata from 'libphonenumber-js/metadata.min'
const metadata = new Metadata(minMetadata)
metadata.country('US')
console.log(metadata.numberingPlan.leadingDigits())
console.log(metadata.numberingPlan.possibleLengths())
console.log(metadata.numberingPlan.IDDPrefix())
console.log(metadata.numberingPlan.defaultIDDPrefix())
As one can see, the Metadata
class is not documented much. Partially, that's because its usage is not encouraged, but it's still used, for example, in react-phone-number-input
to get "leading digits" for a country, or to get maximum phone number length for a country.
Customizing metadata
This library comes prepackaged with three types of metadata.
Sometimes, if only a specific set of countries is needed in a project, and a developer really wants to reduce the resulting bundle size, say, by 50 kilobytes (even when including all regular expressions for validating phone number digits and detecting phone number type), then they can generate such custom metadata and pass it as the last argument to this library's "core" functions.
See generate custom metadata instructions.
First, add metadata generation script to your project's package.json
{
"scripts": {
"libphonenumber-metadata": "libphonenumber-generate-metadata metadata.custom.json --countries RU,DE --extended",
}
}
And then run it like npm run libphonenumber-metadata
.
The arguments are:
- The first argument is the output metadata file path.
--countries
argument is a comma-separated list of the countries included (if --countries
is omitted then all countries are included).--extended
argument may be passed to include all regular expressions for precise phone number validation and getting phone number type, which will enlarge the resulting metadata size approximately twice.--types ...
argument may be passed instead of --extended
to generate metadata that only supports the selected phone number types (a comma-separated list, e.g. --types mobile,fixed_line
). See the list of all possible phone number types. Other phone number types will still be parseable, but they won't be recognized as being "valid" (.isValid()
will return false
), and also their "type" won't be detected (.getType()
will return undefined
).
Then use the generated metadata.custom.json
file with the "core" functions.
Pass the metadata
argument as the last one to the "core" functions.
In ES6 that would be:
import {
parsePhoneNumberFromString as _parsePhoneNumberFromString,
findPhoneNumbersInText as _findPhoneNumbersInText,
AsYouType as _AsYouType
} from 'libphonenumber-js/core'
import metadata from 'libphonenumber-js/metadata.full.json'
function call(func, _arguments) {
var args = Array.prototype.slice.call(_arguments)
args.push(metadata)
return func.apply(this, args)
}
export default function parsePhoneNumberFromString() {
return call(_parsePhoneNumberFromString, arguments)
}
export function findPhoneNumbersInText() {
return call(_findPhoneNumbersInText, arguments)
}
export function AsYouType(country) {
return _AsYouType.call(this, country, metadata)
}
AsYouType.prototype = Object.create(_AsYouType.prototype, {})
AsYouType.prototype.constructor = AsYouType
And for Common.js environment that would be:
var core = require('libphonenumber-js/core')
var metadata = require('libphonenumber-js/metadata.full.json')
function call(func, _arguments) {
var args = Array.prototype.slice.call(_arguments)
args.push(metadata)
return func.apply(this, args)
}
function parsePhoneNumberFromString() {
return call(core.parsePhoneNumberFromString, arguments)
}
exports = module.exports = parsePhoneNumberFromString
exports['default'] = parsePhoneNumberFromString
exports.findPhoneNumbersInText = function findPhoneNumbersInText() {
return call(core.findPhoneNumbersInText, arguments)
}
exports.AsYouType = function AsYouType(country) {
return core.AsYouType.call(this, country, metadata)
}
exports.AsYouType.prototype = Object.create(core.AsYouType.prototype, {})
exports.AsYouType.prototype.constructor = exports.AsYouType
Legacy: How to use the generated metadata.custom.json
file with the legacy "custom" functions.
Pass the metadata
argument as the last one to the "custom" functions.
In ES6 that would be:
import {
parseNumber,
formatNumber,
isValidNumber,
getNumberType,
AsYouType
} from 'libphonenumber-js/custom'
import metadata from 'libphonenumber-js/metadata.full.json'
parseNumber('+78005553535', metadata)
formatNumber({ phone: '8005553535', country: 'RU' }, metadata)
isValidNumber('+78005553535', metadata)
getNumberType('+78005553535', metadata)
new AsYouType('RU', metadata).input('+78005553535')
And for Common.js environment that would be:
var custom = require('libphonenumber-js/custom')
var metadata = require('libphonenumber-js/metadata.full.json')
exports.parseNumber = function parseNumber() {
var parameters = Array.prototype.slice.call(arguments)
parameters.push(metadata)
return custom.parseNumber.apply(this, parameters)
}
exports.formatNumber = function formatNumber() {
var parameters = Array.prototype.slice.call(arguments)
parameters.push(metadata)
return custom.formatNumber.apply(this, parameters)
}
exports.isValidNumber = function isValidNumber() {
var parameters = Array.prototype.slice.call(arguments)
parameters.push(metadata)
return custom.isValidNumber.apply(this, parameters)
}
exports.getNumberType = function isValidNumber() {
var parameters = Array.prototype.slice.call(arguments)
parameters.push(metadata)
return custom.getNumberType.apply(this, parameters)
}
exports.AsYouType = function AsYouType(country) {
custom.AsYouType.call(this, country, metadata)
}
exports.AsYouType.prototype = Object.create(custom.AsYouType.prototype, {})
exports.AsYouType.prototype.constructor = exports.AsYouType
Metadata should be re-generated each time the project is being deployed because Google constantly updates their metadata.
Maintenance
Google periodically releases new metadata with the changes described in the release notes. Sometimes those are minor non-breaking updates, sometimes those are major-version breaking updates.
Metadata update process is automated through an "autoupdate" script: see autoupdate.cmd
(Windows) or autoupdate.sh
(Linux/macOS). The script detects changes to PhoneNumberMetadata.xml
in Google libphonenumber
's repo and if there are changes then it pulls the latest metadata, processes it, commits the changes to GitHub, builds a new version of the library and releases it to NPM. So this library's metadata is supposed to be up-to-date. I could set up this script to run automatically but on my Windows machine ssh-agent
doesn't work properly so I run the "autoupdate" script manually from time to time.
Also Google sometimes (very rarely) updates their code: phonenumberutil.js
(parseNumber()
, formatNumber()
, isValidNumber()
, getNumberType()
), asyoutypeformatter.js
(AsYouType
), PhoneNumberMatcher
(findPhoneNumbersInText()
). The latest sync-up was performed on Jul 29th, 2020.
Contributing
After cloning this repo, ensure dependencies are installed by running:
npm install
This module is written in ES6 and uses Babel for ES5
transpilation. Widely consumable JavaScript can be produced by running:
npm run build
Once npm run build
has run, you may import
or require()
directly from
node.
After developing, the full test suite can be evaluated by running:
npm test
Test coverage must remain at 100%:
npm run test-coverage
When you're ready to test your new functionality on a real project, you can run
npm pack
It will build
, test
and then create a .tgz
archive which you can then install in your project folder
npm install [module name with version].tar.gz
Advertisement
If you're looking for an international "2 days ago" javascript solution then check out javascript-time-ago
.
License
MIT