X Xerobit

Phone Number Regex — Validate and Extract Phone Numbers

Phone number regex patterns for validation and extraction. Covers US phone numbers, international E.164 format, extraction from text, and why regex alone isn't enough for...

Mian Ali Khalid · · 4 min read
Use the tool
Email & URL Extractor
Extract every email address and URL from a block of text. Regex-based, case-insensitive, deduplicated, sorted output.
Open Email & URL Extractor →

Phone number regex is notoriously tricky — formats vary by country, carrier, and context. A regex can enforce basic structure, but production phone validation requires a library like libphonenumber.

Use the Email & URL Extractor to extract contact information from text.

US phone number regex

// Matches common US formats:
// (555) 123-4567  555-123-4567  555.123.4567  5551234567  +1 555 123 4567
const US_PHONE = /^(\+1\s?)?(\(?\d{3}\)?[\s.\-]?)(\d{3}[\s.\-]?\d{4})$/;

// Test:
['(555) 123-4567', '555-123-4567', '5551234567', '+1 555 123 4567'].forEach(p => {
  console.log(p, US_PHONE.test(p));
});

// Extract digits only (for storage):
function normalizePhone(phone) {
  const digits = phone.replace(/\D/g, '');
  // US: strip leading 1 if 11 digits
  if (digits.length === 11 && digits[0] === '1') return digits.slice(1);
  return digits;
}

normalizePhone('(555) 123-4567')  // '5551234567'
normalizePhone('+1-555-123-4567') // '5551234567'

E.164 international format

E.164 is the standard format for international phone numbers: + followed by country code and subscriber number, no spaces or dashes, max 15 digits.

// E.164 validation:
const E164_REGEX = /^\+[1-9]\d{1,14}$/;

E164_REGEX.test('+15551234567')    // true (US)
E164_REGEX.test('+447911123456')   // true (UK)
E164_REGEX.test('+33612345678')    // true (France)
E164_REGEX.test('+1234')           // true (could be valid, short number)
E164_REGEX.test('15551234567')     // false (missing +)
E164_REGEX.test('+1555123456789012') // false (too long)

International phone extraction from text

const PHONE_PATTERNS = [
  // E.164 international
  /\+[1-9]\d{1,14}/g,
  // US format
  /(?:\+1[\s\-.]?)?\(?\d{3}\)?[\s\-.]?\d{3}[\s\-.]?\d{4}/g,
  // UK: +44 7911 123456 or 07911 123456
  /(?:\+44|0)[\s]?\d{4}[\s]?\d{6}/g,
];

function extractPhoneNumbers(text) {
  const found = new Set();
  for (const pattern of PHONE_PATTERNS) {
    const matches = text.match(pattern) || [];
    matches.forEach(m => found.add(m.trim()));
  }
  return [...found];
}

const text = 'Call us: (555) 123-4567 or +44 7911 123456 or +33612345678';
extractPhoneNumbers(text);
// ['(555) 123-4567', '+44 7911 123456', '+33612345678']

Python phone extraction

import re

# Broad pattern for extraction (then validate):
PHONE_PATTERN = re.compile(
    r'(\+?[\d\s\-\.\(\)]{7,20})',
    re.VERBOSE
)

# E.164 only:
E164_PATTERN = re.compile(r'\+[1-9]\d{1,14}\b')

def extract_phones(text: str) -> list[str]:
    return E164_PATTERN.findall(text)

# For production: use phonenumbers library
import phonenumbers

def extract_phones_from_text(text: str, region='US') -> list[str]:
    results = []
    for match in phonenumbers.PhoneNumberMatcher(text, region):
        formatted = phonenumbers.format_number(
            match.number,
            phonenumbers.PhoneNumberFormat.E164
        )
        results.append(formatted)
    return results

text = "Call +1 555-123-4567 or (800) 555-0199 for support"
extract_phones_from_text(text)
# ['+15551234567', '+18005550199']

Why regex isn’t enough

// This passes regex but isn't a real number:
'+12345678901'  // US: area code 234 doesn't exist

// Use google-libphonenumber for full validation:
import { PhoneNumberUtil } from 'google-libphonenumber';
const phoneUtil = PhoneNumberUtil.getInstance();

function isValidPhone(number, regionCode = 'US') {
  try {
    const parsed = phoneUtil.parseAndKeepRawInput(number, regionCode);
    return phoneUtil.isValidNumber(parsed);
  } catch {
    return false;
  }
}

isValidPhone('+15551234567')  // false (555 is fictional)
isValidPhone('+16502530000')  // true (Google's number)

// Format to E.164:
function toE164(number, region = 'US') {
  try {
    const parsed = phoneUtil.parseAndKeepRawInput(number, region);
    return phoneUtil.format(parsed, PNF.E164);
  } catch {
    return null;
  }
}

Input masking (UI)

// Apply US phone mask as user types:
function maskUSPhone(value) {
  const digits = value.replace(/\D/g, '').slice(0, 10);
  if (digits.length <= 3) return digits;
  if (digits.length <= 6) return `(${digits.slice(0,3)}) ${digits.slice(3)}`;
  return `(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`;
}

// Usage in React:
<input
  value={phone}
  onChange={e => setPhone(maskUSPhone(e.target.value))}
  placeholder="(555) 123-4567"
  type="tel"
/>

Related posts

Related tool

Email & URL Extractor

Extract every email address and URL from a block of text. Regex-based, case-insensitive, deduplicated, sorted output.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.