๐ŸŽฎ Live Playground

Adjust props in real-time and see the results instantly

<script setup lang="ts">
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"
    size="normal"
    theme="auto"
    :disabled="false"
    :readonly="false"
    :show-copy="true"
    :show-clear="false"
    :detect="true"
    :with-validity="true"
  />
</template>

1. Basic Usage

Simple phone input with country selector

Phone: โ€”
<script setup lang="ts">
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" />
  <p>Phone: {{ phone || 'โ€”' }}</p>
</template>

2. Phone Validation

Real-time validation state tracking

Enter a phone number
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';

const phone = ref('');
const isValid = ref(false);
</script>

<template>
  <PhoneInput v-model="phone" @validation-change="isValid = $event" />
  <p>{{ isValid ? 'Valid phone number' : phone ? 'Incomplete number' : 'Enter a phone number' }}</p>
</template>

3. Auto Country Detection

Automatically detect country from IP or browser locale

Detected Country: Auto-detecting...
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';

const phone = ref('');
const countryName = ref('');
</script>

<template>
  <PhoneInput v-model="phone" detect @country-change="countryName = $event.name" />
  <p>Detected Country: {{ countryName || 'Auto-detecting...' }}</p>
</template>

4. Size Variants

Three size options: compact, normal, and large

Compact
Normal (default)
Large
<script setup lang="ts">
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" size="compact" />
  <PhoneInput v-model="phone" size="normal" />
  <PhoneInput v-model="phone" size="large" />
</template>

5. Theme Modes

Auto, light, and dark themes

<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';

const phone = ref('');
const themes = ['auto', 'light', 'dark'] as const;
const selectedTheme = ref<(typeof themes)[number]>('light');
</script>

<template>
  <label v-for="theme in themes" :key="theme">
    <input v-model="selectedTheme" type="radio" name="theme" :value="theme" />
    {{ theme }}
  </label>
  <PhoneInput v-model="phone" :theme="selectedTheme" />
</template>

6. Copy & Clear Actions

Built-in copy and clear button functionality

<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';

const phone = ref('');
const notification = ref('');
</script>

<template>
  <PhoneInput
    v-model="phone"
    show-copy
    show-clear
    @copy="notification = 'Copied: ' + $event"
    @clear="notification = 'Input cleared'"
  />
  <p role="status">{{ notification }}</p>
</template>

7. Disabled & Readonly States

Control input interaction modes

Disabled
Readonly
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';

const disabledPhone = ref('1234567890');
const readonlyPhone = ref('9876543210');
</script>

<template>
  <PhoneInput v-model="disabledPhone" disabled />
  <PhoneInput v-model="readonlyPhone" readonly />
</template>

8. Form Integration

Use in forms with validation

{
  "name": "",
  "email": "",
  "phone": ""
}
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css';

const form = ref({ name: '', email: '', phone: '' });
const isValid = ref(false);
const attempted = ref(false);

function submit() {
  attempted.value = true;
  if (isValid.value) console.log(form.value);
}
</script>

<template>
  <form @submit.prevent="submit">
    <input v-model="form.name" name="name" autocomplete="name" aria-label="Full Name" placeholder="Full Name" />
    <input v-model="form.email" name="email" type="email" autocomplete="email" aria-label="Email Address" placeholder="Email Address" />
    <PhoneInput v-model="form.phone" name="phone" @validation-change="isValid = $event" />
    <p v-if="attempted && !isValid">Please enter a complete phone number</p>
    <button type="submit">Submit</button>
  </form>
</template>

๐ŸŽฏ Custom Input Examples

Use directives in Vue and Angular, a hook in React, or actions and attachments in Svelte. Attachments require Svelte 5.29 or later.

9. Basic Custom Input

Apply phone masking to any input element

Value: โ€”
<script setup lang="ts">
import { ref } from 'vue';
import { vPhoneMask, type PMaskPhoneNumber } from '@desource/phone-mask-vue';

const digits = ref('');
const onChange = (phone: PMaskPhoneNumber) => { digits.value = phone.digits; };
</script>

<template>
  <label for="phone">Phone number</label>
  <input id="phone" type="tel" autocomplete="tel"
    v-phone-mask="{ country: 'US', onChange }" />
  <p>Digits: {{ digits }}</p>
</template>

10. Country and Change Events

Configure country and handle change events

Full: โ€”Formatted: โ€”Digits: โ€”
<script setup lang="ts">
import { ref } from 'vue';
import { vPhoneMask, type PMaskPhoneNumber } from '@desource/phone-mask-vue';

const country = ref('GB');
const phoneData = ref<PMaskPhoneNumber>({ digits: '', full: '', fullFormatted: '' });
const onChange = (phone: PMaskPhoneNumber) => { phoneData.value = phone; };
</script>

<template>
  <label for="country">Country</label>
  <select id="country" v-model="country">
    <option value="US">United States</option>
    <option value="GB">United Kingdom</option>
    <option value="DE">Germany</option>
  </select>
  <label for="phone">Phone number</label>
  <input id="phone" type="tel" autocomplete="tel"
    v-phone-mask="{ country, onChange }" />
  <p>Full: {{ phoneData.full }}</p>
  <p>Formatted: {{ phoneData.fullFormatted }}</p>
  <p>Digits: {{ phoneData.digits }}</p>
</template>

11. Automatic Country Detection

Automatically detect country from IP/locale

Detected Country: Detecting...
<script setup lang="ts">
import { ref } from 'vue';
import { vPhoneMask, type PMaskFull } from '@desource/phone-mask-vue';

const countryName = ref('');
const onCountryChange = (country: PMaskFull) => { countryName.value = country.name; };
</script>

<template>
  <label for="phone">Phone number</label>
  <input id="phone" type="tel" autocomplete="tel"
    v-phone-mask="{ detect: true, onCountryChange }" />
  <p>Country: {{ countryName || 'Detectingโ€ฆ' }}</p>
</template>

๐Ÿ”ค Display Formatting

Format phone numbers for text, tables, and contact cards. Choose whether to show the country code and flag.

+1 234 567 8888

Choose a country for numbers without a country code. Complete numbers starting with + can be detected automatically; unrecognized values stay unchanged.

<script setup lang="ts">
import { formatPhone } from '@desource/phone-mask-vue/core';

const phone = "+12345678888";
const country = undefined;
const options = { code: true, flag: false };
</script>

<template>
  <span>{{ formatPhone(phone, country, options) }}</span>
</template>