Always-fresh, extreme small & easy-to-use international phone masking with Google's libphonenumber data
Quick Start • Packages • Features • Demo • Contributing
Phone formats sync automatically from Google's libphonenumber — no stale data, no manual updates. Just upgrade and you're current with global dialing rules.
Real market comparison, segmented by ecosystem and measured for what developers actually ship. Snapshot: 2026-06-15 (Benchmark script, npm Registry API, Bundlephobia package page for independent reference).
Use Total gzip as the primary comparison column.
Gzip is measured locally by this repository benchmark pipeline (isolated temp install + minified bundle build).
Data overhead is additional phone-data gzip excluded from raw package gzip (e.g. required peer engines).
Total gzip = Gzip + Data overhead.
Packages without a phone data source are listed after data-backed packages; each bucket is sorted by Total gzip.
| Package | Last published | Phone data source | Data overhead | Gzip | Total gzip |
|---|---|---|---|---|---|
| @desource/phone-mask · Repo | 2026-05-08 | Included in package | 0.0 KB | 2.8 KB | 2.8 KB |
| libphonenumber-js · Repo | 2026-06-06 | Included in package | 0.0 KB | 43.9 KB | 43.9 KB |
| awesome-phonenumber · Repo | 2026-02-18 | Included in package | 0.0 KB | 74.7 KB | 74.7 KB |
| google-libphonenumber · Repo | 2026-01-19 | Included in package | 0.0 KB | 115.1 KB | 115.1 KB |
Best choice in Core (TypeScript/JavaScript): @desource/phone-mask (2.8 KB).
| Package | Last published | Phone data source | Data overhead | Gzip | Total gzip |
|---|---|---|---|---|---|
| @desource/phone-mask-react · Repo | 2026-05-08 | @desource/phone-mask (dep) | 0.0 KB | 9.4 KB | 9.4 KB |
| react-phone-input-2 · Repo | 2022-07-01 | Included in package | 0.0 KB | 17.1 KB | 17.1 KB |
| mui-tel-input · Repo | 2026-04-24 | libphonenumber-js (dep) | 0.0 KB | 46.7 KB | 46.7 KB |
| react-phone-number-input · Repo | 2026-05-29 | libphonenumber-js (dep) | 0.0 KB | 47.4 KB | 47.4 KB |
| react-international-phone · Repo | 2026-02-21 | None | 0.0 KB | 9.4 KB | 9.4 KB |
Best choice in React: @desource/phone-mask-react (9.4 KB).
React ecosystem note: react-international-phone removed built-in validation in v3 and recommends adding google-libphonenumber separately (migration doc). Raw package gzip above does not include that optional validator overhead.
| Package | Last published | Phone data source | Data overhead | Gzip | Total gzip |
|---|---|---|---|---|---|
| @desource/phone-mask-vue · Repo | 2026-05-08 | @desource/phone-mask (dep) | 0.0 KB | 10.9 KB | 10.9 KB |
| v-phone-input · Repo | 2026-03-11 | awesome-phonenumber (dep) | 0.0 KB | 15.4 KB | 15.4 KB |
| vue-tel-input · Repo | 2026-03-19 | libphonenumber-js (peer: parsePhoneNumberFromString) | 28.5 KB | 10.3 KB | 38.8 KB |
| vue-phone-number-input · Repo | 2022-09-20 | libphonenumber-js (dep) | 0.0 KB | 96.3 KB | 96.3 KB |
Best choice in Vue: @desource/phone-mask-vue (10.9 KB).
| Package | Last published | Phone data source | Data overhead | Gzip | Total gzip |
|---|---|---|---|---|---|
| @desource/phone-mask-svelte · Repo | 2026-05-08 | @desource/phone-mask (dep) | 0.0 KB | 11.3 KB | 11.3 KB |
| svelte-tel-input · Repo | 2026-04-24 | libphonenumber-js (dep) | 0.0 KB | 78.4 KB | 78.4 KB |
Best choice in Svelte: @desource/phone-mask-svelte (11.3 KB).
| Package | Last published | Phone data source | Data overhead | Gzip | Total gzip |
|---|---|---|---|---|---|
| @desource/phone-mask-nuxt · Repo | 2026-05-08 | @desource/phone-mask-vue (runtime: install) | 10.6 KB | 0.8 KB | 11.4 KB |
Best choice in Nuxt: @desource/phone-mask-nuxt (11.4 KB).
Nuxt ecosystem note: there are currently no widely adopted Nuxt-only phone input modules with stable npm + size signals comparable to React/Vue/Svelte peers; most Nuxt apps use Vue phone input packages directly.
Ready-made plugins for your stack:
- ✅ Vue 3 — Component, composable, and directive
- ✅ Nuxt — Auto-imported, SSR-compatible
- ✅ React — Component & hook with modern React patterns
- ✅ Svelte — Component, composable, action, and attachment for Svelte 5
- ✅ TypeScript/Vanilla JS — Framework-agnostic core
| Package | Version | Description |
|---|---|---|
| @desource/phone-mask | Core library — TypeScript/JS | |
| @desource/phone-mask-react | React component + hook | |
| @desource/phone-mask-vue | Vue 3 component + composable + directive | |
| @desource/phone-mask-svelte | Svelte 5 component + composable + action + attachment | |
| @desource/phone-mask-nuxt | Nuxt module |
npm install @desource/phone-mask-reactimport { useState } from 'react';
import { PhoneInput } from '@desource/phone-mask-react';
import '@desource/phone-mask-react/assets/lib.css';
function App() {
const [phone, setPhone] = useState('');
return <PhoneInput value={phone} onChange={setPhone} country="US" />;
}npm install @desource/phone-mask-vue<script setup>
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';
const phone = ref('');
</script>
<template>
<PhoneInput v-model="phone" country="US" />
</template>npm install @desource/phone-mask-svelte<script lang="ts">
import { PhoneInput } from '@desource/phone-mask-svelte';
import '@desource/phone-mask-svelte/assets/lib.css';
let phone = $state('');
</script>
<PhoneInput bind:value={phone} country="US" />npm install @desource/phone-mask-nuxt// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@desource/phone-mask-nuxt']
});<script setup>
import { ref } from 'vue';
const phone = ref('');
</script>
<template>
<PhoneInput v-model="phone" country="US" />
</template>npm install @desource/phone-maskimport { MasksFullMapEn } from '@desource/phone-mask';
import { formatDigitsWithMap } from '@desource/phone-mask/kit';
const mask = MasksFullMapEn.US.mask[0]; // "###-###-####"
const formatted = formatDigitsWithMap(mask, '2025551234').display;
// Result: "202-555-1234"- 🌍 240+ countries with accurate dialing codes and formats
- 🎭 Auto-formatting as you type with smart cursor positioning
- 🔍 Country search with fuzzy matching and keyboard navigation
- 🌐 Auto-detection via GeoIP and browser locale
- 📋 Copy to clipboard with one click
- ✨ Validation with visual feedback
- 🎨 Themeable (light/dark) with custom styling
- ♿ Accessible with ARIA labels and keyboard support
- 📱 Mobile-optimized with proper input modes
- 🌳 Tree-shakeable — only import what you use
- 🔧 TypeScript — full type safety
- 🧩 Directive mode for custom input styling
Try the interactive playground with:
- Real-time formatting preview
- Country switching
- Theme toggle
- Code examples
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/phone-mask.git - Install dependencies:
pnpm install - Create a branch:
git checkout -b feature/my-feature - Make your changes
- Commit:
git commit -m "feat: add awesome feature" - Push:
git push origin feature/my-feature - Open a Pull Request
For package changes intended for release, add a changeset in your PR:
pnpm changeset
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run unit tests with coverage report
pnpm test:unit:coverage
# Start demo dev server
pnpm dev:prepare
pnpm dev:demo
# Generate fresh data from Google's library
pnpm gen
# Refresh README benchmark section (optional, if needed)
pnpm readme:benchmarks
# Lint & format code (if you made changes)
pnpm lint
pnpm lint:fix
pnpm formatDeveloped and maintained by DeSource Labs.
Created by Stefan Popov
MIT © 2026 DeSource Labs