@dazzadev/country-select
v1.0.0
Published
Country and dial code pickers for Vue and React. Country names in any language, resolved from the platform CLDR data.
Maintainers
Readme
@dazzadev/country-select
A country picker and a phone dial-code picker, for Vue (Vuetify) and React (MUI) — plus the underlying country data on its own.
Country names are not shipped with the package: they are resolved at runtime from the platform's
CLDR data through Intl.DisplayNames, so any BCP 47 tag works — fr, ja, ar, pt-BR — not
just a fixed list of translations someone bundled in.
Install
pnpm add @dazzadev/country-selectEntry points
| Import | Contains | Requires |
| -------------------------------- | --------------------------------- | -------------------- |
| @dazzadev/country-select | country data, no framework code | nothing |
| @dazzadev/country-select/vue | CountrySelect, DialCodeSelect | Vue 3.4+, Vuetify 3+ |
| @dazzadev/country-select/react | CountrySelect, DialCodeSelect | React 18+, MUI 6+ |
They're separate so a React app never pulls in Vue (or Vuetify), and vice versa. Import only the one you need.
Data
import { getCountries, getCountry, getDialCodes, flagEmoji } from '@dazzadev/country-select';
getCountries('es')[0];
// { code: 'AF', dialCode: '93', name: 'Afganistán', flag: '🇦🇫' }
getCountry('co', 'ja'); // { code: 'CO', dialCode: '57', name: 'コロンビア', flag: '🇨🇴' }
getCountry('ci', 'pt-BR'); // { code: 'CI', dialCode: '225', name: 'Costa do Marfim', flag: '🇨🇮' }
flagEmoji('CO'); // 🇨🇴The raw array is exported too, as countries, if you would rather build your own picker. It holds
only code and dialCode; Country and LocalizedCountry are exported as types.
Any BCP 47 tag works. An unsupported one falls back to English rather than failing.
getCountries() sorts by name using that locale's collation — Spanish puts Alemania third,
Japanese sorts in kana, Russian in Cyrillic. getDialCodes() sorts by calling code instead, for a
phone prefix picker. Each locale is resolved once and cached.
Vue
<script setup lang="ts">
import { CountrySelect, DialCodeSelect } from '@dazzadev/country-select/vue';
import { ref } from 'vue';
const country = ref('CO');
const dialCode = ref('57');
</script>
<template>
<CountrySelect v-model="country" locale="es" label="Country" />
<DialCodeSelect v-model="dialCode" locale="es" label="Code" />
</template>Both wrap v-autocomplete and forward any extra attribute to it, so density, hide-details,
disabled and the rest work as usual.
React
import { CountrySelect, DialCodeSelect } from "@dazzadev/country-select/react";
<CountrySelect value={country} onChange={setCountry} locale="es" label="Country" />
<DialCodeSelect value={dialCode} onChange={setDialCode} locale="es" label="Code" />Both wrap MUI's Autocomplete and forward any extra prop to it. Use textFieldProps to reach the
underlying TextField.
Styling
The components set no styling of their own: they render the framework's own autocomplete and
forward every extra attribute or prop to it, so your project's defaults apply. In Vuetify that
means a global defaults: { VAutocomplete: { variant: 'solo' } } is respected; in MUI, your theme
is.
No CSS ships with the package. The parts of a row carry stable class names, so style them however you like:
.country-select-flag {
font-size: 1.15rem;
line-height: 1;
margin-right: 8px;
}
/* Only used by the React dial code picker. */
.country-select-name {
flex: 1;
}
.country-select-dial {
opacity: 0.6;
}To change how a row or the selected value is rendered, override the slot in Vue or renderOption
in React:
<CountrySelect v-model="country">
<template #item="{ item, props }">
<v-list-item v-bind="props" :title="`${item.raw.flag} ${item.raw.name} (+${item.raw.dialCode})`" />
</template>
</CountrySelect><CountrySelect
value={country}
onChange={setCountry}
renderOption={(props, option) => (
<li {...props} key={option.code}>
{option.flag} {option.name} (+{option.dialCode})
</li>
)}
/>What the values are
CountrySelect binds the ISO 3166-1 alpha-2 code ('CO'). DialCodeSelect binds the E.164
country calling code without the plus sign ('57').
Calling codes are the ITU ones, so every country in the North American Numbering Plan is 1 —
Trinidad & Tobago is 1, not 1868. That is what you want in front of a phone field: the user
types their national number after it and the result is a valid E.164 number. Thirteen countries
share 1 and two share 7, so the dial picker lists each country separately.
Adding a country
src/data.ts holds 196 sovereign countries as ISO code plus calling code. Add the pair and you are
done — the name comes from the platform in every language.
Requirements
Intl.DisplayNames, available in every current browser and in Node 14+. On Node make sure the
build has full ICU, which is the default since Node 13.
