Skip to content

Examples

Form submission with full phone

Combine callingCode and localPhone to build the full international phone number on submit:

vue
<script setup lang="ts">
import { computed } from 'vue'
import { usePhoneNumber } from '@erag/phone-number-vue'

const { selectedCountry, countryOptions, localPhone, callingCode, isValid, handleInput } =
  usePhoneNumber({ countryCode: 'IN' })

const fullPhone = computed(() =>
  callingCode.value && localPhone.value
    ? `${callingCode.value}${localPhone.value}`
    : ''
)

function submit() {
  if (!isValid.value) return
  console.log('Submitting:', fullPhone.value)
  // e.g. axios.post('/api/register', { phone: fullPhone.value })
}
</script>

<template>
  <form @submit.prevent="submit">
    <select v-model="selectedCountry">
      <option v-for="c in countryOptions" :key="c.key" :value="c">
        {{ c.isoCode2 }} — {{ c.name }} ({{ c.countryCodes?.[0] }})
      </option>
    </select>

    <input
      v-model="localPhone"
      @input="handleInput"
      inputmode="numeric"
      placeholder="Phone number"
    />

    <p v-if="localPhone && !isValid" style="color:red">
      Enter a valid phone number for {{ selectedCountry?.name }}
    </p>

    <button type="submit" :disabled="!isValid">Submit</button>
  </form>
</template>

With Inertia.js form

Use useForm from @inertiajs/vue3 and update the form field inside handleInput:

vue
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3'
import { usePhoneNumber } from '@erag/phone-number-vue'

const form = useForm({ phone: '' })

const { selectedCountry, countryOptions, localPhone, callingCode, isValid, handleInput } =
  usePhoneNumber()

function onPhoneInput(event: Event) {
  handleInput(event)
  form.phone = callingCode.value + localPhone.value
}

function submit() {
  form.post('/profile/update')
}
</script>

<template>
  <form @submit.prevent="submit">
    <select v-model="selectedCountry">
      <option v-for="c in countryOptions" :key="c.key" :value="c">
        {{ c.isoCode2 }} — {{ c.name }}
      </option>
    </select>

    <input
      v-model="localPhone"
      @input="onPhoneInput"
      inputmode="numeric"
      placeholder="Phone number"
    />

    <p v-if="form.errors.phone">{{ form.errors.phone }}</p>

    <button type="submit" :disabled="form.processing || !isValid">Save</button>
  </form>
</template>

Reactive ref as data source

Pass a ref to override bundled data dynamically (e.g. loaded from an API):

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { usePhoneNumber, type PhoneNumberSharedData } from '@erag/phone-number-vue'

const customData = ref<PhoneNumberSharedData>({ countries: [], dialCodes: [] })

const { selectedCountry, countryOptions, localPhone, callingCode, isValid, handleInput } =
  usePhoneNumber(customData)

onMounted(async () => {
  const res = await fetch('/api/phone-countries')
  customData.value = await res.json()
})
</script>

When customData updates, countryOptions and all dependent computed values update reactively.

Styled with Tailwind CSS

vue
<script setup lang="ts">
import { usePhoneNumber } from '@erag/phone-number-vue'

const { selectedCountry, countryOptions, localPhone, callingCode, mask, isValid, handleInput } =
  usePhoneNumber({ countryCode: 'IN' })
</script>

<template>
  <div class="flex flex-col gap-2 max-w-sm">
    <label class="text-sm font-medium text-gray-700">Phone Number</label>

    <div class="flex gap-2">
      <select
        v-model="selectedCountry"
        class="rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500"
      >
        <option v-for="c in countryOptions" :key="c.key" :value="c">
          {{ c.isoCode2 }} +{{ c.countryCodes?.[0] }}
        </option>
      </select>

      <input
        v-model="localPhone"
        @input="handleInput"
        inputmode="numeric"
        :placeholder="mask"
        class="flex-1 rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-green-500"
        :class="{ 'border-red-400': localPhone && !isValid }"
      />
    </div>

    <p class="text-xs text-gray-400">
      {{ callingCode }} · {{ isValid ? '✅ Valid' : 'Enter a valid number' }}
    </p>
  </div>
</template>

MIT License. Copyright Er Amit Gupta