Mark Ku's Blog
Podcast ConversationAI dialogue version of this article · Mandarin audio

Introduction: The Nightmare of Handling International Phone Numbers

Any engineer who has worked on a product for international markets knows the feeling: phone number validation seems simple, but it's actually a bottomless pit. Countries have varying number lengths and convoluted formatting rules. Some countries even allow variable lengths (Indonesia is 10–13 digits). Just listing out the rules is enough to make your head spin. The traditional approach is to brute-force it with a pile of regular expressions (Regex), but as the number of supported countries grows, the Regex quickly balloons into a monster nobody dares to touch. This post shares our team's solution—standing directly on Google's shoulders and using the libphonenumber ecosystem to solve it for both frontend and backend in one go.

Why Maintaining Your Own Regex is a Road to Ruin

Extremely High Maintenance Costs

If you lay out the phone number specifications for various countries, you'll find this is definitely not a "write once and forget" task. Japanese mobile numbers are 11 digits, while landlines are 10. Malaysian mobile numbers are 10–11 digits. Indonesia is even more extreme, where anything from 10 to 13 digits is valid. Stuffing all these rules into Regex not only causes the line count to explode, but if a country changes its rules, making a precise change in that mess of Regex is as difficult as defusing a bomb.

Can't Keep Up with Global Changes

Global telecommunication regulations change more frequently than you might think. Between 2025–2026 alone, the UK updated its National Numbering Plan, Canada implemented Thousands-Block Pooling, and Spain blocked internationally spoofed domestic calls. If you rely on manually updating Regex for these changes, it's not only labor-intensive but also easy to miss updates, leading to validation failures in production—a user enters a perfectly valid number, but the system shows a red error message and blocks them.

Inability to Handle Hidden Details

The most classic example is the "leading zero" problem. Many countries have a local dialing convention that starts with a 0 (e.g., 0912-345-678 in Taiwan, 090-1234-5678 in Japan), but when converted to international format, this 0 must be removed (+886 912345678, +81 9012345678). If you build the input field yourself, users are often confused about "whether or not to include the 0." As a result, the backend receives a bunch of incorrectly formatted data, and downstream systems (like SMS gateways and KYC verification) all fail as a result.

The Industry Standard: Google's libphonenumber Ecosystem

The Core: Google's Commitment

libphonenumber is an open-source phone number handling library maintained by Google, covering the numbering rules for 218 countries/regions worldwide. The latest version is 9.0.32 (released 2026-06-05). Google has a dedicated team that tracks changes to national numbering plans from the ITU and updates the metadata every two weeks. This means as long as you keep your package version updated, Google is essentially shouldering the burden of keeping up with global telecom rules for you.

The Frontend Weapon: react-phone-number-input

In the React ecosystem, react-phone-number-input (v3.4.17) is currently the most popular international phone number input component, with over 2.05 million weekly downloads. Under the hood, it uses libphonenumber-js (v1.13.6), a pure JavaScript rewrite of the original Google library with a bundle size of just 145 KB (compared to the original's ~550 KB). It has native TypeScript support and is more than sufficient for the validation needs of most business applications.

💡 libphonenumber-js does not support emergency numbers, short codes, vanity numbers (like 1-800-GOT-MILK), or number geocoding, but these are rarely needed in typical user registration scenarios.

The Backend Guardian: libphonenumber-csharp

libphonenumber-csharp (v9.0.32) is a direct port of the original Google library by the C# community, with over 92.3 million total downloads on NuGet. It uses scheduled GitHub Actions to automatically sync updates from Google's upstream, with a delay of usually less than 1 day. It supports .NET 8.0 and .NET Standard 2.0.

⚠️ The official documentation specifically warns: If you don't keep the package updated, IsValidNumber might return false for new number formats, causing you to mistakenly block legitimate users.

Architecture Overview

By using packages from the same ecosystem on both the frontend and backend, we create a two-tiered validation architecture—the frontend is responsible for real-time UX feedback, and the backend is responsible for final data gatekeeping:

┌─────────────────────────────────────────────────────┐
│                    使用者瀏覽器                        │
│  ┌───────────────────────────────────────────────┐  │
│  │  react-phone-number-input (v3.4.17)           │  │
│  │  ├─ 國旗選擇 + 國碼自動帶入                      │  │
│  │  ├─ 隨打隨格式化                                │  │
│  │  ├─ 自動去零 (Leading Zero)                    │  │
│  │  └─ 底層:libphonenumber-js (v1.13.6, 145KB)  │  │
│  └───────────────────────────┬───────────────────┘  │
│                              │ E.164 格式            │
│                              │ (+886912345678)       │
└──────────────────────────────┼──────────────────────┘
                               ▼
┌──────────────────────────────┼──────────────────────┐
│                         API Server                   │
│  ┌───────────────────────────┴───────────────────┐  │
│  │  libphonenumber-csharp (v9.0.32)              │  │
│  │  ├─ IsValidNumber() 嚴格驗證                    │  │
│  │  ├─ 解析國碼 + 號碼類型 (手機/市話)               │  │
│  │  └─ 自動同步 Google 上游 (GitHub Actions)       │  │
│  └───────────────────────────┬───────────────────┘  │
│                              │                       │
│                              ▼                       │
│                    資料庫 (E.164 格式儲存)             │
└─────────────────────────────────────────────────────┘

Practical Architecture: Frontend Experience and Backend Validation

Frontend Implementation (React)

Using react-phone-number-input is very intuitive. A few lines of code can create an input field with country code selection, real-time formatting, and smart flag switching:

import PhoneInput, { isValidPhoneNumber } from 'react-phone-number-input'
import 'react-phone-number-input/style.css'

function PhoneForm() {
  const [phone, setPhone] = useState<string | undefined>()

  const handleSubmit = () => {
    if (phone && isValidPhoneNumber(phone)) {
      // phone 已經是 E.164 格式,例如 "+886912345678"
      api.post('/register', { phone })
    }
  }

  return (
    <PhoneInput
      international
      defaultCountry="TW"
      countries={['TW', 'JP', 'US', 'MY', 'ID', 'SG', 'TH']}
      value={phone}
      onChange={setPhone}
      placeholder="輸入電話號碼"
    />
  )
}

The UX improvements from this code are:

  • As-you-type formatting: Entering a Japanese number automatically formats it as 090 1234 5678, which is clean and clear.
  • Smart paste detection: When a user pastes +62 812 3456 7890, the country flag automatically switches to Indonesia 🇮🇩.
  • Automatic leading zero removal: After selecting Taiwan, if a user enters 0912345678, the component automatically converts it to +886 912345678.
  • Ability to restrict the country list: Use the countries prop to display only the countries your business serves.

📊 Live Demo: https://catamphetamine.github.io/react-phone-number-input/

Backend Validation (C#)

Frontend validation is primarily for UX; the backend is the final line of defense. Use libphonenumber-csharp for strict validation:

using PhoneNumbers;

public class PhoneValidator
{
    private static readonly PhoneNumberUtil PhoneUtil = PhoneNumberUtil.GetInstance();

    public static bool ValidatePhone(string phoneNumber)
    {
        try
        {
            // 解析號碼(第二個參數為預設國碼區域,傳 null 表示號碼必須包含 "+" 國碼)
            var number = PhoneUtil.Parse(phoneNumber, null);

            // 嚴格驗證:檢查號碼長度、格式是否符合該國規則
            if (!PhoneUtil.IsValidNumber(number))
                return false;

            // 取得號碼類型(手機、市話、VOIP 等)
            var numberType = PhoneUtil.GetNumberType(number);

            // 可依業務需求限制只接受手機號碼
            return numberType == PhoneNumberType.MOBILE
                || numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE;
        }
        catch (NumberParseException)
        {
            return false;
        }
    }
}

Best Practice: Store Everything in E.164 Format

Regardless of the format received from the frontend, always convert it to E.164 format (+國碼號碼, e.g., +886912345678) before storing it in the database. The benefits are:

AspectAdvantages of E.164
Format ConsistencyAny number from anywhere in the world has the same format, eliminating confusion over "with/without leading zero."
Downstream CompatibilityServices like Twilio and AWS SNS all require E.164.
Query ConvenienceWhen checking for uniqueness in the database, you don't have to worry about multiple representations of the same number.
Length ConstraintA maximum of 15 digits (including country code), making field design straightforward.

Business Considerations: Is It Free for Commercial Use?

PackageLicenseCommercial UseClosed SourceViral (Copyleft)
react-phone-number-inputMIT✅ Fully permitted✅ No need to disclose source code❌ No
libphonenumber-jsMIT✅ Fully permitted✅ No need to disclose source code❌ No
libphonenumber-csharpApache 2.0✅ Fully permitted✅ No need to disclose source code❌ No

In short, both MIT and Apache 2.0 licenses are very friendly to commercial and closed-source projects. You can freely use, modify, and distribute the code, and even package it within paid software for sale, without any of the "forced open-sourcing" viral risks associated with licenses like GPL. You can confidently integrate them into production products without worrying about licensing issues.

Conclusion: Say Goodbye to Maintenance Nightmares and Focus on Your Core Business

Let's review the core benefits of adopting the libphonenumber ecosystem:

  • 🚀 Maintenance cost approaches zero: Google updates global telecom rules every two weeks; you just need to run npm update / dotnet update.
  • 📊 Significant improvement in data quality: Real-time frontend formatting + strict backend validation prevents junk data from entering your database.
  • Professional user experience: Country flag selection, as-you-type formatting, and smart paste detection make your forms look like they belong to a world-class product.

This isn't just a technical choice; it's a strategic decision. Offload the tedious, non-core tasks to Google and the open-source community, allowing your team to focus on the business logic that truly creates value. If your project still has a tangled mess of Regex for different countries, now is the perfect time to refactor.

References

Author

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。Read More

Found this useful?

The author's free tools, daily podcasts and newsletter are all here.

Mark Ku · This article is licensed under CC BY 4.0. Credit the author and link back to the original when reusing it.

Comments

Subscribe to Newsletter

Subscribe to get new posts delivered instantly — never miss a tech share.

By submitting, you agree to receive emails. You can anytime.

Popular Posts

View all
Mark Ku
··599

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution
Mark Ku
··493

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.
Mark Ku
··333

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki
Mark Ku
··271

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning
Mark Ku
··222

Setting Up Samba on Ubuntu to Share Folders with Windows 11

Setting Up Samba on Ubuntu to Share Folders with Windows 11
Mark Ku
··217

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1
用 Google libphonenumber 套件 處理多國電話號碼驗證(前端 + 後端) - Mark Ku's Tech Notes