Check whether your own phone numbers pass this pattern

Paste the numbers you actually have — from a contact list, a form submission, a spreadsheet column — into the tester below and see which pass and which fail, in whatever punctuation style people actually typed them. The sample already loaded mixes four valid North American numbers with two that fail — one simply too short, the other a perfectly valid UK number this NANP-only pattern was never built to accept. The copy-paste pattern, explained token by token, and where international numbers break it, follow underneath.

/ / gm
Flags

    Your pattern and test text never leave this tab. Matching runs in a same-origin Web Worker with a timeout, so a real phone number pasted in for testing is never sent anywhere. That protection matters more than it looks: a single regular expression once caused a real, global outage — the timeout above is what keeps this page safe from the same failure mode.

    ^\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$

    Say this plainly before anything else

    This pattern is scoped to the North American Numbering Plan (NANP) — the US, Canada and a handful of Caribbean countries that share the +1 country code and the 10-digit area-code-plus-subscriber-number shape. There is no such thing as a single regex that correctly validates phone numbers worldwide: the E.164 international format alone allows anywhere from 7 to 15 digits after the country code, and individual countries layer their own area-code and trunk-prefix conventions on top of that. A pattern that tried to encode all of it correctly would essentially have to embed a lookup table of country-specific rules, which is what dedicated libraries such as Google’s libphonenumber actually do, in code, not in one regular expression.

    Token by token

    TokenMeaning
    ^Start of the string
    \+?An optional literal +, for the international-prefix style +1 555…
    1?An optional literal 1, the NANP country code, with or without the preceding +
    [-.\s]?An optional separator: hyphen, dot, or a single whitespace character
    \(?An optional literal opening parenthesis around the area code
    \d{3}The three-digit area code
    \)?An optional literal closing parenthesis
    [-.\s]?Another optional separator, between the area code and the exchange
    \d{3}The three-digit exchange code
    [-.\s]?Another optional separator, before the last block
    \d{4}The four-digit subscriber number
    $End of the string

    Every separator and every piece of punctuation is independently optional. That is what lets 5551234567, 555-123-4567 and (555) 123-4567 all match the same pattern, but it is also a trade-off: because parentheses are optional rather than paired, a malformed string like (555 123-4567 (a stray opening parenthesis with no closing one) still matches, since each \(?/\)? is judged independently rather than as a matching pair.

    What it matches

    • 555-123-4567 — hyphen-separated, no country code.
    • (555) 123-4567 — parenthesised area code, space then hyphen.
    • +1 555.123.4567 — international prefix, dot-separated.
    • 5551234567 — all ten digits, no punctuation at all.

    What it deliberately does not match, and why

    InputVerdictWhy
    12345RejectedToo few digits — not a 10-digit NANP number of any kind
    555-123-456RejectedOne digit short in the last block — \d{4} requires exactly four
    call me maybeRejectedNot digits at all
    +44 20 7946 0958RejectedA real UK number — correctly rejected, because it is not NANP-shaped, but this is the pattern’s central limitation: it has no way to say “valid, just not for this country” versus “not a phone number”
    555.123.4567 ext 204RejectedTrailing extension text after the last digit block fails the $ anchor — extensions are common enough in business numbers that you may want to strip /\s*(ext\.?|x)\s*\d+$/i before validating, rather than trying to fold it into this pattern

    Why one pattern cannot cover international numbers

    Phone numbering is a per-country administrative scheme, not a universal format. Even just within NANP, the pattern above accepts area codes that do not exist (a NANP area code cannot start with 0 or 1, something this pattern does not check) because encoding the full valid range would make an already-verbose pattern considerably harder to read for a small real-world gain — unallocated area codes are rare enough in practice that most applications accept the small false-positive rate. Outside NANP, the differences compound: variable total length, different trunk prefixes (many countries use a leading 0 domestically that is dropped when dialled with the country code), and mobile versus landline numbers that are sometimes distinguishable only by a prefix specific to that country’s plan. Google’s libphonenumber, used by Android, iOS, and most serious phone-input UI libraries, encodes this as per-country metadata rather than as a regex, precisely because a regex cannot reasonably hold it. If your form needs to accept phone numbers from anywhere, a library with that metadata, paired with a country selector, is a better foundation than any single pattern.

    Flavour: this is JavaScript’s regex

    This pattern targets JavaScript’s built-in RegExp, per ECMA-262. It uses only literals, \d, character classes and ?/{n} quantifiers — all supported identically in PCRE and Python’s re, so it needs no changes to move between them. See the cookbook’s flavour-honesty section for the constructs that do differ between engines.

    Worked examples

    1. Normalising before you store it, not just validating it

    Three different customers might type 555-123-4567, (555) 123-4567 and 5551234567 for the same number. All three pass this pattern, but storing them in three different text shapes makes searching and deduplication painful later. Strip everything that is not a digit (value.replace(/\D/g, '')) after validating, and store the 10 (or 11, with country code) plain digits — format only for display.

    2. Redacting phone numbers from pasted text

    With g on and the Replace tab, this pattern can scrub phone-number-shaped text out of a block of pasted support tickets or chat logs before sharing it externally. Because the pattern is permissive about separators, it will catch more formatting variants than a stricter one would — useful for redaction, where a false positive (redacting something that merely looks like a phone number) is a far smaller problem than a false negative (leaving a real one exposed).

    3. Rejecting an obviously fake number in a signup form

    000-000-0000 and 123-456-7890 both match this pattern — it checks digit count and punctuation shape, not whether the number is a real, dialable line. If you need to filter out these obvious placeholders specifically, add an explicit denylist check for them; a regex checking “the digits are not all the same” or “not a simple ascending run” adds complexity most forms do not need for a problem this narrow.

    4. Validating on blur, formatting on display

    A common pattern in phone-number input fields: leave the raw field editable and unformatted while the person types, run this validator only when the field loses focus, and if it passes, replace the displayed value with a consistently formatted version ((555) 123-4567, say) built from the digits alone. That separates three concerns this single regex was never meant to handle together — accepting varied input, validating shape, and presenting a consistent format — into three small, individually simple steps.

    Using this pattern in your own code

    The pattern relies only on \d, character classes and ?/{n} quantifiers, all standard across mainstream regex engines, so it needs no adjustment moving between a browser form and a server-side check.

    // JavaScript
    const PHONE_RE = /^\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
    PHONE_RE.test('(555) 123-4567');  // true
    PHONE_RE.test('12345');           // false
    # Python's re module — identical pattern text
    import re
    PHONE_RE = re.compile(r'^\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$')
    bool(PHONE_RE.match('+1 555.123.4567'))  # True
    # grep -P, scanning a support-ticket export for anything phone-shaped
    grep -Po '\+?1?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}' tickets.txt

    The command-line example above drops the ^/$ anchors and adds -o deliberately — on the command line the more common job is finding phone-shaped substrings inside longer lines, not validating that an entire line is only a phone number, so the anchored form from the pattern box would be the wrong tool for that particular job even though the core pattern is identical.

    Common mistakes when adapting this pattern

    • Bolting a country selector onto this exact pattern. A common, well-intentioned mistake: add a dropdown for country code, keep this NANP-shaped pattern underneath, and just swap the leading 1? for whatever code the dropdown selects. The rest of the pattern — the fixed 3-3-4 digit grouping — is specific to NANP and wrong for most other countries’ numbering, so the digit-count portion needs to change per country too, not just the prefix.
    • Making every separator required instead of optional. Changing [-.\s]? to [-.\s] (dropping the ?) to “enforce consistent formatting” also rejects 5551234567 typed with no punctuation at all, which is one of the more common ways people actually enter a phone number on a mobile keyboard. Normalise the format after validating, rather than restricting what counts as valid input.
    • Forgetting that \d is not locale-aware. JavaScript’s \d matches only the ASCII digits 0–9, even with the u flag set — it does not expand to Arabic-Indic or other Unicode decimal digit forms someone might paste from a phone keyboard set to a different script. If your users might type digits from another numbering system, normalise them to ASCII digits before validating, rather than trying to widen \d itself.
    • Treating a pattern match as proof of a working number. This is worth repeating in the specific context of adapting the pattern: adding more punctuation variants or a stricter area-code range does not change what the pattern fundamentally is — a shape check. No amount of regex refinement gets you deliverability; only an SMS or voice verification code sent to the number does.

    Frequently asked questions

    Does this validate international phone numbers?

    No. It is scoped to the North American Numbering Plan (+1). See the section above on why one regex cannot reasonably cover every country’s numbering scheme, and consider a metadata-driven library such as libphonenumber if you need real international coverage.

    Why does (555 123-4567 (unbalanced parenthesis) still match?

    Because \(? and \)? are each independently optional — the pattern does not enforce that they appear in a pair. Catching unbalanced parentheses specifically would need an alternation between the “with parens” and “without parens” forms written out separately, which most real-world uses of this pattern have not found worth the added complexity.

    Can I extend it to accept an extension, like “ext. 204”?

    Yes — append an optional group such as (?:\s*(?:ext\.?|x)\s*\d{1,5})? before the closing $. It is kept out of the base pattern here so the core 10-digit shape stays easy to read; add it only if your data actually contains extensions.

    Should I validate on every keystroke or only on submit?

    Only on blur or submit, generally. A phone number is typed digit-by-digit, and it will fail this pattern at every incomplete stage until the last character — validating on every keystroke just shows the user a permanent error state while they are still typing.

    Does this check whether the number is currently in service?

    No, and no client-side regex could — that requires querying a carrier or a third-party lookup service over the network, which is out of scope for a pattern that only inspects the text in front of it.

    Why does a leading 1 without a + also match?

    Because 1? does not require the preceding + — both 1 555 123 4567 and +1 555 123 4567 are common ways people type the country code, so the pattern accepts either, or neither.

    Does 15551234567, with the country code and no separator at all, match?

    Yes — 1? allows the leading 1 with no punctuation around it at all, and every [-.\s]? in the pattern is optional, so all eleven digits run together still satisfies every group in sequence: 1, then three digits, then three, then four. This is one of the more common ways a phone number arrives already stripped of formatting from another system, so accepting it without punctuation is deliberate, not an oversight.

    Is my phone number sent anywhere when I test it here?

    No. Matching runs inside a same-origin Web Worker, and there is no upload endpoint anywhere on this page. Nothing you paste into the test-text box leaves your browser.

    Limitations

    • NANP (+1) only — not a general international pattern.
    • Does not validate that an area code or exchange code is actually allocated.
    • Parentheses are independently optional, not enforced as a matching pair.
    • No support for extensions unless you extend the pattern yourself (see FAQ above).
    • Cannot confirm the number is in service — that needs a network lookup this client-side tool does not make.

    Related