How to Validate Phone Number Input in HTML and JavaScript

January 09, 2023
Written by
Reviewed by
Diane Phan
Twilion
Liz Moy
Twilion

If you've ever looked up "phone number regex" and regretted it, you're in the right place. There are a lot of valid phone number formats, but fortunately there are free tools that you can use to help make sure a phone number is valid.

This post will walk through two ways to check a phone number's validity: the Twilio Lookup API and the intl-tel-input JavaScript plugin. This builds on How to build international phone number input in HTML and JavaScript, which you can reference for more details on building the nice-looking phone number input field I'm using below.

You can find the finished code on my GitHub.

Why you should validate phone number input

You want to validate phone numbers so that you can help prevent sign up spam and fraud and also catch simple errors like typos. We'll include recommendations for phone verification and some more account security best practices at the end since you'll also usually want to make sure that the user has access to the phone number they're providing, not just that it's a valid number.

Setting up phone number validation

You might already have a phone number input, but if you're starting from scratch you can use a basic HTML page that accepts a phone number input.

This form uses the intl-tel-input plugin which processes the input to the international E.164 standard. This format is used by many APIs, including Twilio's, to ensure standardized input and global support. You can read more about how to build a phone number input field that can provide the E.164 format in this blog post.

This is what I'll be using to show off the validation options described below. Put this inside a file named index.html:

<!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>
    <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 class="alert alert-info" style="display: none"></div>
      <div class="alert alert-error" style="display: none"></div>
    </div>
  </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",
    });

    const info = document.querySelector(".alert-info");
    const error = document.querySelector(".alert-error");

    function process(event) {
      event.preventDefault();

      const phoneNumber = phoneInput.getNumber();

      info.style.display = "";
      info.innerHTML = `Phone number in E.164 format: <strong>${phoneNumber}</strong>`;
    }
  </script>
</html>

For nicer styles you can grab the stylesheet from my GitHub and place it in a document named styles.css in the same folder as index.html.

Test out the application by loading the HTML file in a web browser. You'll notice that an invalid phone number doesn't throw any errors:

 

invalid phone number input with no error

 

Let's fix that!

How to validate phone numbers

There are two options we'll walk through for validating phone numbers:

  1. Using the Twilio Lookup API
  2. Using the intl-tel-input plugin

Validate phone numbers with the Twilio Lookup API

The Twilio Lookup API request is free, and you can embed this in any backend you would like. We'll be using Twilio's serverless JavaScript functions in this tutorial.

  • Pros: The Lookup API has updated phone number validity data. You can also use the API to check for line type or carrier.
  • Cons: It's more code and a network request.

For this part, you'll need a Twilio account - sign up for free. Head over to the Twilio console and create a function service, I called mine intl-tel-input

 

twilio function service named intl-tel-input

Add a function and call it lookup

twilio function named lookup

Make sure that you set the function type to "public", which is necessary since we'll be calling this outside of Twilio.

lookup function set to public

Replace the code with the following:

function errorStr(errors) {
  return errors
    .map((err) => {
      return err.replaceAll("_", " ").toLowerCase();
    })
    .join(", ");
}

exports.handler = function (context, event, callback) {
 const response = new Twilio.Response();
 response.appendHeader("Content-Type", "application/json");
 response.appendHeader('Access-Control-Allow-Origin', '*');
 response.appendHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
 response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');

 if (typeof event.phone === "undefined") {
   response.setBody({
     success: false,
     error: "Missing parameter; please provide a phone number.",
   });
   response.setStatusCode(400);
   return callback(null, response);
 }

 const client = context.getTwilioClient();
 const lookup = await client.lookups.v2.phoneNumbers(event.phone).fetch();

 if (lookup.valid) {
      response.setStatusCode(200);
      response.setBody({
        success: true,
      });
      callback(null, response);
    } else {
      response.setStatusCode(400);
      response.setBody({
        success: false,
        error: `Invalid phone number ${event.phone}: ${errorStr(
          lookup.validationErrors
        )}`,
      });
      callback(null, response);
    }
  } catch (error) {
    console.error(error);
    response.setStatusCode(error.status);
    response.setBody({
      success: false,
      error: "Something went wrong.",
    });
    callback(null, response);
  }
};

This function will look up the phone number and return "success: true" if the API determines it is valid, and "false" if it determines it is not. In the case of an invalid phone number, the function will also return a comma separated list of reasons like "too short", "too long", "invalid country code" or "not a number".

Hit "Deploy All" at the bottom.

You can test your function in the browser by adding a query parameter: http://<your-prefix-here>.twil.io/lookup?phone=+18448144627

You should see {"success":true}

Now let's call this from our application.

Replace the process function with the following. Make sure to replace the URL inside of the fetch call with your twilio function URL:

function process(event) {
 event.preventDefault();

 const phoneNumber = phoneInput.getNumber();

 info.style.display = "none";
 error.style.display = "none";

 const data = new URLSearchParams();
 data.append("phone", phoneNumber);

 fetch("http://<your-url-here>.twil.io/lookup", {
   method: "POST",
   body: data,
 })
   .then((response) => response.json())
   .then((json) => {
     if (json.success) {
       info.style.display = "";
       info.innerHTML = `Phone number in E.164 format: <strong>${phoneNumber}</strong>`;
     } else {
       console.error(json.error);
       error.style.display = "";
       error.innerHTML =  json.error;
     }
   })
   .catch((err) => {
     error.style.display = "";
     error.innerHTML = `Something went wrong: ${err}`;
   });
}

Now if you try to input an invalid number you'll see an error message and corresponding reason:

invalid phone number &#x27;123&#x27;: &#x27;too short&#x27;

 

Validate phone numbers with the intl-tel-input plugin

Follow the instructions in this post or in the plugin documentation to add intl-tel-input to your site or use the HTML code provided above. In addition to building nice input forms, the intl-tel-input plugin provides a wrapper around Google's libphonenumber to help detect valid and invalid phone numbers.

  • Pros: Less code, especially if you're already using the plugin for phone number input
  • Cons: Valid phone numbers change, and you'll be relying on plugin updates to catch updates in libphonenumber. You might lock out some valid users.

Replace the process function code with the following:

function process(event) {
 event.preventDefault();

 const phoneNumber = phoneInput.getNumber();

 info.style.display = "none";
 error.style.display = "none";

 if (phoneInput.isValidNumber()) {
   info.style.display = "";
   info.innerHTML = `Phone number in E.164 format: <strong>${phoneNumber}</strong>`;
 } else {
   error.style.display = "";
   error.innerHTML = `Invalid phone number.`;
 }
}

The result should be the same as the Lookup API version!

Both are solid options for validating phone numbers, I'm partial to using the Lookup API since you'll probably be making an API request at this point anyway to start a phone verification and store the user in your database.

Best practices for account security

Validating a phone number is only one way to help prevent fraud and ensure you're protecting your application and your users' data.

I always recommend phone verification - you can do this in a few ways but sending a one-time passcode (OTP) to the phone number is a great way to ensure possession the first time a user provides this information. This helps protect against both simple typos and a user inputting a number they do not own. Check out this project on the Twilio Code Exchange to learn more about implementing OTPs with the Twilio Verify API.

You might also be interested in:

I can't wait to see what you build.