Monday, 1 February, 2021 UTC


Summary

Phone numbers are standardized in an international format known as E.164 which combines country codes and subscriber numbers in a format like this: +14155552671. This format is required by many APIs (including Twilio's) and means that you don't have to store country codes and phone numbers in two separate database columns.
However, you probably don't want your users to have to type in a + sign and country code when they provide their phone number to:
  • Register a new account
  • Enable SMS 2FA
  • Request a callback from customer service
  • Sign up for marketing notifications
This blog post will walk through how to build a phone number input field to process and parse phone numbers using basic HTML, JavaScript, and the intl-tel-input plugin. We'll include recommendations for phone verification and fraud prevention.
You can find the finished code on my GitHub.

What can the intl-tel-input plugin do?

This project makes heavy use of intl-tel-input, a "JavaScript plugin for entering and validating international telephone numbers". I'll cover my usual setup of the plugin, but it has a lot of additional configuration options that you can explore in the documentation.
The plugin provides a country code drop down with nice flags to represent different countries. It also processes the subscriber or "national format" number to normalize user input that could include spaces, parentheses, dashes, and more.

Embed the intl-tel-input plugin in your code

We're going to keep things pretty simple here and start with some vanilla HTML and JavaScript. Create a new file called index.html and add the following code:
<!DOCTYPE html> <html lang="en"> <head> <title>International telephone input</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="styles.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/css/intlTelInput.css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/intlTelInput.min.js"></script> </head> <body> </body> </html> 
This pulls in the CDN versions of the plugin's CSS and JS, which helps us display the country code drop down and process the phone numbers. I'm using v17.0.8, but you can find the latest version by checking out the tags. You can also install the plugin with a bundler or download and host the source code yourself.
For nicer styles, create a new file in the same folder called styles.css and add the CSS found here.
Next, add the form that will include the phone number input. Inside the body tags of the index.html file, add the following HTML:
<div class="container"> <form id="login" onsubmit="process(event)"> <p>Enter your phone number:</p> <input id="phone" type="tel" name="phone" /> <input type="submit" class="btn" value="Verify" /> </form> </div> 
If you load this file into a browser at this point, you'll see the form field but no formatting yet, we still need to initialize the plugin.

Initialize the intl-tel-input plugin

Add a script tag below the body but inside the HTML and add the following initialization code:
 <!-- ^^ form code ^^ --> </body> <script> const phoneInputField = document.querySelector("#phone"); const phoneInput = window.intlTelInput(phoneInputField, { utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js", }); </script> </html> 
The utilsScript is technically optional, but it provides a lot of really useful functionality like country specific placeholders and the E.164 formatting we'll use below.
Reload the page and you should see the country picker and a placeholder now.

Process the phone number input to get the international format

Add an alert banner below our form. These will help us display our results.
 </form> <!-- ^^ form code ^^ --> <div class="alert alert-info" style="display: none;"></div> 
Next, add a function to handle the form submit. Inside your <script> tag and after the plugin initialization, add the following:
const info = document.querySelector(".alert-info"); function process(event) { event.preventDefault(); const phoneNumber = phoneInput.getNumber(); info.style.display = ""; info.innerHTML = `Phone number in E.164 format: <strong>${phoneNumber}</strong>`; } 
The most important part here is phoneInput.getNumber() — this is the plugin code that converts the selected country code and user input into the international format.
Reload the page, enter a phone number and you should see the international format!

Bonus: Make the plugin location-aware

The plugin defaults to the US, but you're probably implementing this because you have global users. You can update the settings to default to the user's location based on their IP address.
Sign up for a free account at IPinfo and grab your access token. You could use a different IP address API for this if you have a different one you prefer. Add the following function at the top of your <script> tag before defining the phoneInputField object:
function getIp(callback) { fetch('https://ipinfo.io/json?token=<your token>', { headers: { 'Accept': 'application/json' }}) .then((resp) => resp.json()) .catch(() => { return { country: 'us', }; }) .then((resp) => callback(resp.country)); } 
Then update the intl-tel-input initialization to include two parameters: initialCountry: "auto" and geoIpLookup: getIp.
const phoneInput = window.intlTelInput(phoneInputField, { initialCountry: "auto", geoIpLookup: getIp, utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js", }); 
Now if I set my VPN to Austria, the plugin will update the default country

Bonus: Add preferred countries

One of my other favorite features is the "preferred countries" setting, which allows you to specify countries to appear at the top of the list. For this example I've identified that most of my users are in the US, Colombia, India, and Germany and prioritized those options.
You can update this setting where you initialize the plugin by adding an array of preferredCountries in ISO 3166-1 alpha-2 code format:
const phoneInput = window.intlTelInput(phoneInputField, { preferredCountries: ["us", "co", "in", "de"], utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.8/js/utils.js", }); 

Validate phone number input

You might notice that this form doesn't actually prevent any invalid phone numbers yet. You can follow along this post on how to validate phone number input so we don't have people submitting things like this:

Best practices for phone verification

You definitely want to normalize phone numbers in E.164 format, but if you plan on storing those numbers you also will want to perform a phone verification. Phone verification helps ensure that the user didn't accidentally type in the wrong number, that they have control over the number they're associating with their account, and that you're not spamming the wrong person - you get the idea. Luckily Twilio's Verify API makes it easy to do phone verification via SMS or voice calling. Check out this project on our Code Exchange to learn how to implement one-time passcodes (you're halfway there with the phone number input!).
You might also be interested in:
  • Twilio Lookup API documentation
  • Serverless phone verification with Twilio Verify
  • How to incentivize users to enable 2FA
  • How to build a fast checkout with SMS verification using Stripe and Twilio
Questions about phone number input and verification? You can find me on Twitter @kelleyrobinson. I can't wait to see what you build.