Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including @botique/libphonenumber-js with all npm packages installed. Try it out:

var libphonenumberJs = require("@botique/libphonenumber-js")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

@botique/libphonenumber-js v1.0.27

A simpler (and smaller) rewrite of Google Android's popular libphonenumber library

libphonenumber-js

npm version npm downloads coverage

A simpler (and smaller) rewrite of Google Android's famous libphonenumber library: easy phone number parsing and formatting in javascript.

See Demo

LibPhoneNumber

libphonenumber is a phone number formatting and parsing library released by Google, originally developed for (and currently used in) Google's Android mobile phone operating system. Implementing a rigorous phone number formatting and parsing library was crucial for the phone OS overall usability (back then, in the early 2000s, it was originally meant to be a phone after all, not just a SnapChat device).

libphonenumber-js is a simplified pure javascript port of the original libphonenumber library (written in C++ and Java because those are the programming languages used in Android OS). While libphonenumber has an official javascript port which is being maintained by Google, it is tightly coupled to Google's closure javascript utility framework. It still can be compiled into one big bundle which weighs 220 KiloBytes — quite a size for a phone number input component. It can be reduced to a specific set of countries only but that wouldn't be an option for a worldwide international solution.

One part of me was curious about how all this phone matching machinery worked, and another part of me was curious if there's a way to reduce those 220 KiloBytes to something more reasonable while also getting rid of the closure library and rewriting it all in pure javascript. So, that was my little hackathon for a couple of weeks, and seems that it succeeded. The resulting library does everything a modern web application needs while maintaining a much smaller size of about 120 KiloBytes.

Difference from Google's libphonenumber

  • Pure javascript, doesn't require any 3rd party libraries.
  • Metadata size is just about 80 KiloBytes while the original libphonenumber metadata size is about 200 KiloBytes.
  • Doesn't parse alphabetic phone numbers like 1-800-GOT-MILK.
  • Doesn't parse or format "carrier codes": they're only used in Colombia and Brazil, and only when dialing within those countries from a mobile phone to a fixed line number.
  • Doesn't parse or format special local-only phone numbers: emergency phone numbers like 911, "short codes", numbers starting with a *, etc.
  • Doesn't parse or format phone numbers with "dial out codes". The "dial out codes" are the prefixes prepended in order to call to another country. E.g. an international phone number + ... would be called as 00 ... from Europe and 011 ... from the United States. The reason for not supporting "dial out codes" is that I don't see any use case where a user would input a phone number with a "dial out code" instead of using the + sign notation.

Installation

npm install libphonenumber-js --save

Usage

import { parse, format, AsYouType } from 'libphonenumber-js'

parse('8 (800) 555 35 35', 'RU')
// { country: 'RU', phone: '8005553535' }

format('2133734253', 'US', 'International')
// '+1 213 373 4253'

new AsYouType().input('+12133734')
// '+1 213 373 4'
new AsYouType('US').input('2133734')
// '(213) 373-4'

Country code definition

"Country code" means either a two-letter ISO country code (like US) or a special 001 country code used for non-geographical entities (as per Google's libphonenumber library). For example, +7 800 555 35 35 phone number belongs to Russia so it has RU country code where as +800 1 1111 1111 phone number could belong to any country so it has 001 country code.

API

parse(text, [defaultCountry], [options])

Attempts to parse a valid 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 } where

  • country is a country code
  • phone is a national (significant) number
  • ext is a phone number extension

If the phone number supplied isn't valid then an empty object {} is returned.

parse('+1-213-373-4253') === { country: 'US', phone: '2133734253' }
parse('(213) 373-4253', 'US') === { country: 'US', phone: '2133734253' }

// Parses phone number extensions.
parse('(213) 373-4253 ext. 123', 'US') === { country: 'US', phone: '2133734253', ext: '123' }

// Parses RFC 3966 phone number URIs.
parse('tel:+78005553535;ext:123') === { country: 'RU', phone: '8005553535', ext: '123' }

Available options:

  • defaultCountry : string — Same as defaultCountry argument.

  • extended : boolean — If set to true then parse() will attempt to parse "possible" phone numbers even if they're classified as "invalid". The result of "extended" parsing has shape { country, countryCallingCode, phone, ext, valid: boolean, possible: boolean }; some or all of these properties may be absent. The "extended" parsing 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"). Though I don't know who might need such an advanced feature, still it has been requested and has been implemented.

Speaking of phone number extensions, I myself consider them obsolete and I'd just discard the extension part given we're in the 21st century. Still, some people asked for phone number extensions support so it has been added. But I personally think it's an unnecessary complication.

format(parsedNumber, format, [options])

Formats a parsedNumber into a string according to a format.

Available formats:

  • National — e.g. (213) 373-4253
  • International — e.g. +1 213 373 4253
  • E.164 — e.g. +12133734253
  • RFC3966 (the phone number URI) — e.g. tel:+12133734253;ext=123

Available options:

{
  formatExtension(number, extension) — Formats `number` and `extension` into a string.
                                       By default returns `${number} ext. ${extension}`.
}

parsedNumber argument must be an already parse()d phone number (to strip national prefix from it). That means that first a phone number is parse()d and only then is it format()ted and there's no other way around it. For example, a phone number is parse()d before storing it in a database and then it is forrmat()ted each time it is read from the database. The parsedNumber object argument can also be expanded into two string arguments (for those who prefer this kind of syntax):

format({ country: 'US', phone: '2133734253' }, 'International') === '+1 213 373 4253'
format('2133734253', 'US', 'International') === '+1 213 373 4253'

// The following won't work because the phone number argument is invalid
// (has not been parsed previously and therefore contains the `0` national prefix)
format('017212345678', 'DE', 'E.164') !== '+4917212345678'

// Formats phone number extensions (except for E.164).
format({ country: 'US', phone: '2133734253', ext: '123' }, 'National') ===  '(213) 373-4253 ext. 123'

getNumberType(number, [defaultCountry])

Determines phone number type (fixed line, mobile, toll free, etc). This function will work if --extended (or relevant --types) metadata is available (see Metadata section of this document). The regular expressions used to differentiate between various phone number types consume a lot of space (two thirds of the total size of the --extended library build) therefore they're not included in the bundle by default.

The number argument can be either a result of the parse() function call — { country, phone } — or a string possibly accompanied with defaultCountry.

getNumberType('9160151539', 'RU') === 'MOBILE'
getNumberType({ phone: '9160151539', country: 'RU' }) === 'MOBILE'

isValidNumber(number, [defaultCountry])

Checks if a phone number is valid.

The number argument can be either a result of the parse() function call — { country, phone } — or a string possibly accompanied with defaultCountry.

isValidNumber('+1-213-373-4253') === true
isValidNumber('+1-213-373') === false

isValidNumber('(213) 373-4253', 'US') === true
isValidNumber('(213) 37', 'US') === false

isValidNumber({ phone: '2133734253', country: 'US' }) === true

The difference between using parse() 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 parse('123456', 'DE') would return { country: 'DE', phone: '123456' } because this phone number matches the general phone number rules for Germany. But, if the metadata is compiled with --extended (or relevant --types) flag (see below) and the precise regular expressions for possible phone numbers are included in the metadata then isValidNumber() is gonna use those precise regular expressions for validation and isValid('123456', 'DE') will return false because the phone number 123456 doesn't actually exist in Germany.

So, the general phone number rules for a country are mainly for phone number formatting: they dictate how different phone numbers (matching those general regular expressions) should be formatted. And parse() uses only those general regular expressions (as per the reference Google's libphonenumber implementation) to perform basic phone number validation. isValidNumber(), on the other hand, is all about validation, so it digs deeper into precise regular expressions (if they're included in metadata) for possible phone numbers in a given country. And that's the difference between them: parse() parses phone numbers and loosely validates them while isValidNumber() validates phone number precisely (provided the precise regular expressions are included in metadata).

By default those precise regular expressions aren't included in metadata at all because that would cause metadata to grow twice in its size (the complete metadata size is about 130 KiloBytes while the default reduced metadata size is about 70 KiloBytes). If anyone needs to use (or generate) custom metadata then it's very easy to do so: follow the instructions provided in the Customizing metadata section of this document.

Using phone number validation feature

I personally wouldn't rely on Google's phone number validation too much because it might get outdated:

  • First, new phone number rules are added to Google's libphonenumber library after they have already been implemented in real life (which introduces a delay).

  • Then those new rules from Google's libphonenumber are updated automatically in this library (this scheduled update script introduces a small delay of 1 day, unless it malfunctions).

  • And then there's still the web application itself using this library and until a developer installs libphonenumber-js@latest and redeploys the web application it's gonna use the old (outdated) phone number validation rules which could result in losing customers with perfectly valid but brand new phone numbers.

Phone number validation rules are constantly changing for --extended rules and are fairly static for "general" ones. Still imagine a web application (e.g. a promosite or a "personal website") being deployed once and then running for years without any maintenance.

class AsYouType(defaultCountry)

Creates a formatter for partially entered phone number. The defaultCountry is optional and, if specified, is gonna be the default country for formatting non-international phone numbers. The formatter instance has two methods:

  • input(text) — Takes any text and appends it to the input. Returns the formatted phone number.
  • reset() — Resets the input.

The formatter also has the following getters:

  • country — Phone number country.
  • getNationalNumber() — Returns the national number part of the phone number.
  • template — The template used to format the phone number. Digits (and the + sign, if present) are denoted by x-es.
new AsYouType().input('+12133734') === '+1 213 373 4'
new AsYouType('US').input('2133734') === '(213) 373-4'

const asYouType = new AsYouType()
asYouType.input('+1-213-373-4253') === '+1 213 373 4253'
asYouType.country === 'US'
asYouType.getNationalNumber() === '2133734253'
asYouType.template === '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. This solution is sufficient for all use cases except for phone number extensions which Google's "As You Type" formatter does not support. If your project requires phone number extensions input then use a separate input field for that.

getCountryCallingCode(country)

There have been requests for a function returning a country calling code by country code.

getCountryCallingCode('RU') === '7'
getCountryCallingCode('IL') === '972'

Metadata

Metadata is generated from Google's original PhoneNumberMetadata.xml by transforming XML into JSON and removing unnecessary fields.

Currently I have a script set up monitoring changes to PhoneNumberMetadata.xml in Google's repo and automatically releasing new versions of this library when metadata in Google's repo gets updated. So this library's metadata is supposed to be up-to-date. Still, in case the automatic metadata update script malfunctions some day, anyone can request metadata update via a Pull Request here on GitHub:

  • Fork this repo
  • npm install
  • npm run metadata:update:branch
  • Submit a Pull Request to this repo from the update-metadata branch of your fork

npm run metadata:update:branch command creates a new update-metadata branch, downloads the new PhoneNumberMetadata.xml into the project folder replacing the old one, generates JSON metadata out of the XML one, checks if the metadata has changed, runs the tests, commits the new metadata and pushes the commit to the remote update-metadata branch of your fork.

Alternatively, a developer may wish to update metadata urgently, without waiting for a pull request approval. In this case just perform the steps described in the Customizing metadata section of this document.

React

There's also a React component utilizing this library: react-phone-number-input

Examples

For those asking for phone number examples for use in <input placeholder/>s there's examples.mobile.json.

RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free