Phone number toolkit for JavaScript and TypeScript.

Parsing, validation, formatting, and a headless input controller. Zero dependencies.

100% geographic parity, verified against Google libphonenumberAbout 4x faster parsing, measured in CI

Phone inputvalid
馃寪
RegionNumber typeE.164Error

Get started

npm install @telixon/core

Await ensureEngineReady() once to load the engine. Everything after that is a plain function call.

quickstart.ts
import { ensureEngineReady, parsePhoneNumber } from '@telixon/core';
await ensureEngineReady();
const number = parsePhoneNumber('+1 (415) 555-0132');
number.isValid(); // true
number.getRegion(); // 'US'
number.getNumberType(); // 'FIXED_LINE_OR_MOBILE'
number.formatE164(); // '+14155550132'

Verified against Google

The conformance suite loads Google libphonenumber's own source at the exact commit the engine was compiled from, so there is no version drift between the library and its reference.

On every push to main and every pull request, CI replays the whole conformance corpus against Google's source, across every supported region, and fails the build on any divergence. Once a week, an exhaustive enumeration re-proves the result across every input the engine can distinguish, at every length. The input controllers go through the same gate: every input is typed character by character, and the controller's resolved number must equal the parser's.

Explore the results on the live dashboard.

1.8B+
inputs in the weekly exhaustive proof
Regions245Methods compared12Allowlisted divergences0

Parity is measured over geographic regions. Non-geographic ranges (Google's region 001, for example +800) are out of scope.

Compiled Engine

Telixon compiles Google libphonenumber's metadata and rules into a deterministic finite automaton (DFA), stored as compact binary tables. Most libraries interpret those rules at runtime, on every parse. Telixon does that work once, ahead of time.

Parsing a number is a walk over the DFA, one digit per step. The state the walk ends on already holds the region, the number type, whether the number is valid, and how to format it.

The engine is lazy by design. Its tables stay out of your initial bundle until you call ensureEngineReady(), which loads and decodes them off the main thread. You choose the moment; nothing loads at import time.

One digit, one step

USFixed line or mobileValid
119 KB
the whole world's numbering rules, compiled and compressed
<1%
of one display frame per keystroke, at the 99th percentile, even at 360 Hz
Every PR
performance-gated, so a regression cannot merge quietly

Measured by the benchmark suite in the repository. Full, dated runs on the live benchmarks.

Input Controller

A complete API for a phone-number field. It handles every action a real input has: insert, delete forward or backward, replace a selection, undo, redo. The caret and the format stay correct on every keystroke.

Hard case, Argentina mobile

In Argentina, a mobile number carries two dialing prefixes that aren't part of the number itself: a leading 0 and a mobile 15. So Telixon runs a full round-trip on every keystroke:

  1. Strips the 0 and the 15, adds the mobile 9, and gets the canonical national number.
  2. Determines the region, number type, and format.
  3. Restores the 0 and the 15, then formats it.

The caret survives the whole strip-and-restore round-trip.

See how Argentine mobile numbers are structured.

馃嚘馃嚪
E.164+5491123456789

Wire it yourself

Each editing method takes the current value and selection, and returns the next value and caret.

import {
createNationalInputController,
} from '@telixon/core';
const controller = createNationalInputController({
defaultRegion: 'AR',
});
controller.insert('011 15-2345-678', '9', 15, 15);
// '011 15-2345-6789', caret 16
controller.deleteBackward('011 15-2345-6789', 16, 16);
// '011 15-2345-678', caret 15
controller.undo(); // '011 15-2345-6789'
controller.redo(); // '011 15-2345-678'

Or let web-sdk do it

createPhoneInput binds the controller to a real <input> and handles beforeinput, paste, drop, word delete, and IME.

import {
createPhoneInput,
} from '@telixon/web-sdk';
const phoneInput = createPhoneInput({
mode: 'national',
input: document.querySelector('input')!,
defaultRegion: 'AR',
});
phoneInput.subscribe(({ validationError }) => {
hint.textContent = validationError?.kind ?? '';
});
phoneInput.getPhoneNumber().formatE164();
// '+5491123456789' for the value above

The full controller API lives in the docs.

Developer experience

What it is like to have Telixon in a codebase.

+ Structured errors

Validation failures return typed reasons with the data needed to act, never a bare boolean.

partial.getValidationError()
// { kind: 'TOO_SHORT', minLength: 10 }

+ Closed types

Region and number-type unions are generated from the metadata.

controller.setRegion('USA')
// compile error: not assignable to RegionCode

+ Headless

No CSS and no image assets anywhere. You bring the markup; Telixon brings the state.

+ Nothing on import

Importing a package does no work: no fetches, no globals, no side effects.

+ Tree-shakeable

ESM, named exports only, sideEffects false. Bundlers drop what you don't use.

+ Every format

E.164, international, national, and RFC 3966, from one parsed number.

+ Synchronous entry

A separate sync-init entry initializes the engine where async won't do.

+ Region list

A searchable, locale-aware region list with a stable order, as pure state.

+ Flag emoji

Flags derived arithmetically from Unicode letters, one pure function, no assets.

+ Real placeholders

Each region's example number, formatted the way the field will format input.

Packages

Telixon is an ecosystem built around one core, with thin layers you add only where you need them.

@telixon/coreTodayThe engine, the full query API, and the input controller. Runs in Node, browsers, edge runtimes, and workers; one import resolves to the right build for each.@telixon/web-sdkTodayThe DOM layer for the input controller. You bring your own markup.
@telixon/web-anatomyPlannedThe styling contract for the web packages: stable class names, semantic tokens, and state attributes.
@telixon/web-themePlannedA ready-made stylesheet implementing the web-anatomy contract.
@telixon/web-componentsPlannedA drop-in phone input as a standard web component.
@telixon/angularPlannedThe same controller with idiomatic Angular bindings.
@telixon/reactPlannedThe same controller with idiomatic React bindings.
@telixon/vuePlannedThe same controller with idiomatic Vue bindings.

Every layer is optional. Take the raw API and build exactly the behavior you need, or take a ready-made layer.