Skip to content

Migration to v6

Before reading

VPhoneInput has been fully rewritten for v6 to improve its features and provide a new usePhoneInput composable for integrations outside Vuetify. Many API have changed, so you should read every section of this guide. If you have any question, fill free to open an issue on the repository.

This migration guide is mostly using plugin configuration as example of API changes, but you can also apply those changes to the component properties if that's what you are using.

Vue and Vuetify support

VPhoneInput now requires Vue v3.5.0 or up, and Vuetify v3.10.7 or up.

Better TypeScript support

VPhoneInput component's typings have been improved to strictly reflect the available properties, slots, events or exposed values. This may cause legitimate TypeScript errors which were not emitted previously.

CSS styles import

CSS styles import path for VPhoneInput have renamed from v-phone-input/dist/v-phone-input.css to v-phone-input/styles to match Vuetify styles import path.

ts
import 'v-phone-input/dist/v-phone-input.css';
import 'v-phone-input/styles';

Country input mode

Previously, VPhoneInput component shipped with default VSelect input for country selection. This behavior created unused code being packaged into production builds when always using enableSearchingCountry property (to display an autocomplete input instead of a select).

To avoid this, enableSearchingCountry property has been removed, and you must now pass either selectPhoneCountryInput or autocompletePhoneCountryInput to the component props or the plugin factory. In addition, it is not necessary to register VAutocomplete anymore when using the autocomplete country input.

ts
import { selectPhoneCountryInput } from 'v-phone-input';

createVPhoneInput({
  ...selectPhoneCountryInput,
});
ts
import { autocompletePhoneCountryInput } from 'v-phone-input';

createVPhoneInput({
  // ...
  enableSearchingCountry: true,
  ...autocompletePhoneCountryInput,
});

Country display

Country display component have been renamed:

  • VCountryIconSvg renamed to VPhoneCountryFlagSvg
  • VCountryIconSprite renamed to VPhoneCountryFlagSprite

Country display customization property has been renamed to countryDisplayComponent (previously countryIconMode). In order to reduce package's footprint, svg and sprite possible special values have been removed, and must be replaced with new country display component:

ts
import { VPhoneCountryFlagSvg } from 'v-phone-input';

createVPhoneInput({
  // ...
  countryIconMode: 'svg',
  countryDisplayComponent: VPhoneCountryFlagSvg,
});
ts
import { VPhoneCountryFlagSprite } from 'v-phone-input';

createVPhoneInput({
  // ...
  countryIconMode: 'sprite',
  countryDisplayComponent: VPhoneCountryFlagSprite,
});
ts
createVPhoneInput({
  // ...
  countryIconMode: MyCustomCountryIconComponent,
  countryDisplayComponent: MyCustomCountryIconComponent,
});

Country guessing

Country guessing is now done through a function instead of an object. countryGuesser property is now named guessCountry, and guessCountry boolean property has been removed. To disable country guessing (which remains the default behavior), you must now pass a null or undefined value to guessCountry property. In addition, Ip2cCountryGuesser is replaced by guessPhoneCountry.

ts
import { Ip2cCountryGuesser } from 'v-phone-input';
import { guessPhoneCountry } from 'v-phone-input';

createVPhoneInput({
  // ...
  countryGuesser: new Ip2cCountryGuesser(),
  guessCountry: guessPhoneCountry(),
});
ts
import { Ip2cCountryGuesser } from 'v-phone-input';

createVPhoneInput({
  // ...
  countryGuesser: new Ip2cCountryGuesser(),
  guessCountry: false,
  guessCountry: null,
});

Finally, MemoIp2cCountryGuesser and StorageMemoIp2cCountryGuesser have been removed. You can easily create a memoized country guesser using the new memoizeGuessPhoneCountry function. Here are implementation you can use to replace the removed guesser:

ts
import { MemoIp2cCountryGuesser } from 'v-phone-input';
import { guessPhoneCountry, memoizeGuessPhoneCountry } from 'v-phone-input';

let country: string | null = null;
const guessCountry = memoizeGuessPhoneCountry(
  guessPhoneCountry,
  () => country,
  (nextCountry) => {
    country = nextCountry;
  },
);

createVPhoneInput({
  // ...
  countryGuesser: new MemoIp2cCountryGuesser(),
  guessCountry: guessPhoneCountry(),
});
ts
import { StorageMemoIp2cCountryGuesser } from 'v-phone-input';
import { guessPhoneCountry, memoizeGuessPhoneCountry } from 'v-phone-input';

const guessCountry = memoizeGuessPhoneCountry(
  guessPhoneCountry,
  () => window.localStorage.getItem('v_phone_input_country') || null,
  (nextCountry) => window.localStorage.setItem('v_phone_input_country', nextCountry || null),
);

createVPhoneInput({
  // ...
  countryGuesser: new StorageMemoIp2cCountryGuesser(),
  guessCountry: guessPhoneCountry(),
});

Phone number formatting behavior

Prior v6, phone formatting would occur every time the phone number was valid, and immediately. This could lead to unindented input formatting while the user is still typing. To mitigate this issue, formatting is now done on (country or phone) input blur.

We do not recommend this, but you can restore the previous behavior by defining displayFormatDelay property to 0.

ts
createVPhoneInput({
  // THIS IS NOT RECOMMENDED!
  displayFormatDelay: 0,
});

Validation

To reduce code complexity, rules property of the VPhoneInput component does not support phone or context additional parameters anymore. This reduces VPhoneInput code complexity for easier maintenance and tinier build size when this niche feature is not used. If you are relying on those parameters, you should:

  • Rely on available country two-way binding to store the country value inside a ref, and use it later or to produce an example phone number;
  • Parse the phone yourself from the rule's value, for example using parsePhoneNumber from awesome-phonenumber.

Here is a complete example to migrate some/all of your parametized rules usage:

vue
<script
  lang="ts"
  setup
>
import { getExample, parsePhoneNumber } from "awesome-phonenumber";
import { computed, ref } from "vue";

const phone = ref('');

const country = ref('FR');
const label = computed(() => "Phone");
const example = computed(() => getExample(country.value).number?.national ?? '');

const rules = computed(() => [
  (value, phone, { country, label, example }) => {
    console.log(phone, country, label, example);
  },
  (value) => {
    const phoneObject = parsePhoneNumber(value ?? '', { regionCode: country.value });
    // equivalent in v6:
    console.log(phone, country, label, example);
  },
]);
</script>

<template>
  <v-phone-input
    v-model="phone"
    v-model:country="country"
    :label="label"
    :rules="rules"
  />
</template>

Slots changes

country-input and phone-input could previously be used to provide custom input components to the input. Those two slots are now removed. If you need to fine-tune phone number inputs components, you should now use usePhoneInput composable.

In addition, country-icon slot has been renamed to country-display.

Other slots - direct slots like country-name or passed-down slots like country-input:prepend-icon - remain.

Properties changes

In addition to other changes specified in this guide, multiple properties changed:

  • example is now receiving an object with a country property instead of a country object;
  • disableGuessLoading has been replaced by guessLoading, which does the opposite;
  • invalidMessage with null will no longer disable validation, but instead will make the validation rule returns false instead of a message. If you want to disable validation, use validate with null instead.
  • phoneValidator has been renamed to validate;
  • wrapperProps has been renamed to wrapperAttrs;