VPhoneInput
International phone field for Vuetify 3 and Vue 3.
Coming from 2.x.x and upgrading to Vuetify 3?
Checkout the migration guide.
Wish to use this package with Vuetify 2?
Old version 2.x.x
is compatible with Vuetify 2 and Vue 2.
Proudly supported by the CoWork'HIT.
Motivation: There are already multiple libraries to provide phone number input on Vuetify. But
for those already published are not actively maintained or does not put focus on providing great
accessibility. This new library aims to provide those two.
Demo
You can try the VPhoneInput with many options and plugin registration code generation on
the GitHub pages demo.
TL;DR
Installation though Yarn:
yarn add v-phone-input flag-icons
Installation though NPM:
npm install v-phone-input flag-icons
Plugin installation:
import 'flag-icons/css/flag-icons.min.css';
import 'v-phone-input/dist/v-phone-input.css';
import { createVPhoneInput } from 'v-phone-input';
const vPhoneInput = createVPhoneInput({
countryIconMode: 'svg',
});
app.use(vPhoneInput);
Component usage:
<script setup>
import { ref } from 'vue';
const phone = ref('');
</script>
<template>
<v-phone-input v-model="phone" />
</template>
Documentation
Table of contents
Requirements
VPhoneInput requires Vue@3
and Vuetify@3
to be installed and working in your project.
VPhoneInput utilizes recent ES features that may require polyfills for older browser like
Internet Explorer.
Available country guessers requires fetch.
Installation
You can install this package though Yarn:
yarn add v-phone-input flag-icons
Or NPM:
npm install v-phone-input flag-icons
flag-icons
package is required if you want the input to display countries' flags.
Usage
You can globally define the input using the provided plugin factory. This will register
the v-phone-input
component globally. You must also import the package additional CSS.
import 'flag-icons/css/flag-icons.min.css';
import 'v-phone-input/dist/v-phone-input.css';
import { createVPhoneInput } from 'v-phone-input';
import { createApp } from 'vue';
const app = createApp(App);
const vPhoneInput = createVPhoneInput({
countryIconMode: 'svg',
});
app.use(vPhoneInput);
You may also only define the field on a per-file basis. Notice that with this method, you won't be
able to define props' default values for the input.
<script setup>
import 'flag-icons/css/flag-icons.min.css';
import 'v-phone-input/dist/v-phone-input.css';
import { VPhoneInput } from 'v-phone-input';
import { ref } from 'vue';
const phone = ref('');
</script>
<template>
<v-phone-input
v-model="phone"
country-icon-mode="svg"
/>
</template>
Migration
Please follow the migration guide if you need to migrate from version 1, 2 or 3.
Props
VPhoneInput provides many props to customize the input behaviors or display.
You may pass those props directly the input:
<template>
<v-phone-input label="Your Phone number" />
</template>
Or define them as default values when creating the plugin:
import 'v-phone-input/dist/v-phone-input.css';
import { createVPhoneInput } from 'v-phone-input';
import { createApp } from 'vue';
const app = createApp(App);
const vPhoneInput = createVPhoneInput({
label: 'Your phone number',
});
app.use(vPhoneInput);
Name | Type | Default | Description |
---|
label | MessageResolver | Phone | The phone input label (see Localization). |
ariaLabel | MessageResolver | undefined | The phone input aria-label (see Localization). |
countryLabel | MessageResolver | Country | The country input label (see Localization). |
countryAriaLabel | MessageResolver | Country for {label} | The phone input aria-label (see Localization). |
placeholder | MessageResolver | undefined | The phone input placeholder (see Localization). |
hint | MessageResolver | undefined | The phone input hint (see Localization). |
invalidMessage | MessageResolver | The "{label}" field is not a valid phone number (example: {example}). | The phone input message when number is invalid (see Localization). |
example | string or Function or undefined | undefined | Example of a valid phone number (or factory function which takes a Country[] object) to customize phone number example for message. |
countryIconMode | string or VueConstructor or undefined | undefined | The country icon display mode (see Country icon modes). |
allCountries | Country[] | An array of all possible countries | Array of countries to use. |
preferCountries | CountryOrIso2[] | [] | Array of countries' codes to propose as first option of country input. |
includeCountries | CountryOrIso2[] | [] | Array of countries' codes to include as options of country input. |
excludeCountries | CountryOrIso2[] | [] | Array of countries' codes to exclude from options of country input. |
defaultCountry | CountryOrIso2 | undefined | Default country to select when not guessing nor detecting from current value. |
countryGuesser | CountryGuesser or PreferableCountryGuesser | new MemoIp2cCountryGuesser() | Country guesser implementation to use when guessing country (see Country guesser). |
guessCountry | boolean | false | Enable guessing country using default or provided country guesser. |
disableGuessLoading | boolean | false | Disable passing the country input in a loading state when guessing country. |
enableSearchingCountry | boolean | false | Turns the country input into a VAutocomplete input (see Enabling searching countries example). |
rules | Function[] or string[] | [] | Additional rules to pass to phone input (see Validation example). |
displayFormat | PhoneNumberFormat | 'national' | Format to use when displaying valid phone numbers in phone text input (see Phone number formats). |
country | string | '' | Country of the country input. Can be used with .sync modifier. Will be superseded by value's country if defined on mounting. |
value | string | '' | Value of the phone input. You may use it using v-model or @input . |
countryProps | object | {} | Props to pass to the VSelect or VAutocomplete country input component. |
phoneProps | object | {} | Props to pass to the VTextField phone input component. |
You can also pass the Vuetify VTextField
and Vuetify VSelect
props to the component to customize variant, icons, errors, etc. using the v-bind
directive or
the countryProps
and phoneProps
props.
Events
Name | Type | Description |
---|
update:modelValue | string | Emitted when the country or phone is updated with the E164 formatted phone number. |
update:country | CountryIso2 | Emitted when the country is updated with the selected country. |
You can also bind to the Vuetify VTextField
and Vuetify VSelect
events
using the v-bind
directive or the countryProps
and phoneProps
props.
Slots
Each slots with a country:
prefix are passed to the country input, other slots are passed to the
phone input.
<template>
<v-phone-input>
<template #country:label>
Label for country
</template>
<template #label>
Label for phone
</template>
</v-phone-input>
</template>
The input also provides two special slots: the country-icon
slot for countries' icons
display, country-name
slot for countries' name display and country-append
slot for countries'
list items appended info display.
<template>
<v-phone-input>
<template #country-icon="{ country, decorative }">
<img
:src="`path/to/flags/${country.iso2}.png`"
:alt="decorative ? '' : country.name"
>
</template>
<template #country-name="{ country }">
{{ country.name }}
</template>
<template #country-append="{ country }">
<strong>+{{ country.dialCode }}</strong>
</template>
</v-phone-input>
</template>
Examples
Country icon modes
With VPhoneInput, you can choose between 5 country icon modes which are changing the way the country
input will display.
SVG
This is the proposed way to use the input. Rely on an SVG flag icons package. You must
install flag-icons
package to use it.
import 'flag-icons/css/flag-icons.min.css';
const vPhoneInput = createVPhoneInput({
countryIconMode: 'svg',
});
Sprite
Rely on a CSS sprite flag icons package. You must
install world-flags-sprite
package to use it.
import 'world-flags-sprite/stylesheets/flags32.css';
const vPhoneInput = createVPhoneInput({
countryIconMode: 'sprite',
});
Custom component
This allows you to register a custom component to display country icons. Component will
receive country
and decorative
props. We provide a simple VPhoneCountrySpan
component to
simplify using a CSS class image based icon system (such as another CSS sprite file).
import { defineComponent, h } from 'vue';
const vPhoneInput = createVPhoneInput({
countryIconMode: defineComponent({
setup(props) {
return () => h('span', {}, [props.country.name]);
},
}),
});
Custom slot
See the slots section.
No icon
This is the default behavior when not overriding options or props default values. This will not
display an icon inside the list, but will show the ISO-2 code inside the selection slot of country
input.
Validation
By default, the input will validate that the phone number is a valid one by injecting a rules to
the phone text input.
You may add any additional rules by providing a rules
prop to the input:
<script setup>
const rules = [
(value, phone, { label, country, example }) => !!value || `The "${label}" field is required.`,
];
</script>
<template>
<v-phone-input :rules="rules" />
</template>
Any rule you pass as a function will receive 3 arguments (instead of one for default Vuetify rules)
that you may use when validating user's input:
If you don't want the automatic validation to run, you can pass a null
value to the
invalid-message
prop.
Enabling searching countries
You may provide a enableSearchingCountry
with a true
value to enable textual search in
countries.
Since VPhoneInput does not import VAutocomplete to reduce its weight, you might need to provide
this component to Vue when treeshaking Vuetify components
(e.g. when using vite-plugin-vuetify
).
When using plugin registration
To enable searching countries for all inputs as a default behavior, you must
register the VAutocomplete
component globally inside your plugin definition.
import 'flag-icons/css/flag-icons.min.css';
import 'v-phone-input/dist/v-phone-input.css';
import { createVPhoneInput } from 'v-phone-input';
import { VAutocomplete } from 'vuetify/components';
app.component('VAutocomplete', VAutocomplete);
const vPhoneInput = createVPhoneInput({
enableSearchingCountry: true,
});
app.use(vPhoneInput);
After this setup, all v-phone-input
will be using an autocomplete input
for country by default.
When using per-file registration
To enable searching countries on a per-file basis, you must register the
VAutocomplete
component inside your app definition.
import { VAutocomplete } from 'vuetify/components';
app.component('VAutocomplete', VAutocomplete);
After this setup, you can safely enable the enable-searching-country
property.
<script setup>
import 'flag-icons/css/flag-icons.min.css';
import 'v-phone-input/dist/v-phone-input.css';
import { VPhoneInput } from 'v-phone-input';
</script>
<template>
<v-phone-input enable-searching-country />
</template>
Customizing display format
By default, valid phone number will be formatted using the national format. You can customize the
display format using the displayFormat
prop/option:
const vPhoneInput = createVPhoneInput({
displayFormat: 'international',
});
Localization
Localizable props may be defined on a per-input basis:
<template>
<v-phone-input
label="Phone number"
country-label="Country"
country-aria-label="Country for phone number"
invalid-message="Phone number must be a valid phone number (example: 01 23 45 67 89)."
/>
</template>
Localizable props can also be defined for all inputs as a default behavior:
const vPhoneInput = createVPhoneInput({
label: 'Phone number',
countryLabel: 'Country',
countryAriaLabel: ({ label }) => `Country for ${label}`,
invalidMessage: ({ label, example }) =>
`${label} must be a valid phone number (example: ${example}).`,
});
import i18n from './path/to/i18n-plugin';
const vPhoneInput = createVPhoneInput({
label: i18n.global.t('phone.phoneLabel'),
countryLabel: i18n.global.t('phone.phoneCountry'),
countryAriaLabel: (options) => i18n.global.t('phone.phoneCountryFor', options),
invalidMessage: (options) => i18n.global.t('phone.invalidPhoneGiven', options),
});
Any localizable prop is a message resolver. Notice that for label
and ariaLabel
props, no label will be defined for the message resolver's options.
To disable a message, you should pass null
(instead of undefined
). This way, you'll be able
to disable the country label for example (be sure to provide an explicit countryAriaLabel
when doing so).
Dependencies
VPhoneInput relies on multiple dependencies to work:
Types
Country
A country ISO-2 code is a string containing 2 uppercase characters representing a country (e.g. FR
for France).
A country object contains information about a country.
type CountryIso2 = string;
interface Country {
name: string;
iso2: CountryIso2;
dialCode: string;
}
export type CountryOrIso2 = Country | CountryIso2;
Country Guesser
A country guesser is a class implementing CountryGuesser
interface and providing a guess
method
to detect the default country to use.
interface CountryGuesser {
guess: () => Promise<CountryIso2 | undefined>;
}
This package ships with two CountryGuesser
implementations:
Ip2cCountryGuesser
which uses IP2C service to guess the country from the
client's IP. Notice that IP2C service might not work when using an add blocking extension.MemoIp2cCountryGuesser
(default) which memoize the result of Ip2cCountryGuesser
promise into a
class property.StorageMemoIp2cCountryGuesser
which memoize the result of Ip2cCountryGuesser
promise into a
storage implementation (defaults to localStorage
).
A preferable country guesser is a country guesser with the capability to use the user preference
instead of the guessed country on future calls.
interface PreferableCountryGuesser extends CountryGuesser {
setPreference: (country: CountryIso2) => void;
}
MemoIp2cCountryGuesser
and StorageMemoIp2cCountryGuesser
are implementations of
the PreferableCountryGuesser
interface. They store the country (when changed using the input) to
return it instead of the guessed country on future guess
call.
Phone Number Formats
A phone number format is a string representing a format, allowing to change the display format of a
phone number in input. Here are the available format (provided
by awesome-phonenumber):
e164
: +46707123456
international
: +46 70 712 34 56
national
: 070-712 34 56
rfc3966
: tel:+46-70-712-34-56
significant
: 707123456
Message options
An object containing the input label
(or aria-label
if no label) and an example of a valid phone
number for active country.
type MessageOptions = {
label?: Message;
country: Country;
example: string;
}
Message
A type representing a localized message for the input which will be used as the label, hint, etc.
export type Message = string | undefined;
Message resolver
A type representing a function to resolve a message using current message options or directly the
message.
export type MessageResolver = ((options: MessageOptions) => Message) | Message;
Contributing
Please see CONTRIBUTING file for more details.
Informal discussion regarding bugs, new features, and implementation of existing features takes
place in the
GitHub issue page of this repository.
Credits
Inspired by vue-tel-input
and vue-tel-input-vuetify.
License
v-phone-input
is an open-sourced software licensed under the
MIT license.