Vanilla JS Country Dropdown with Flag Icons & Phone Codes – CountrySelect Pro

Category: Form , Javascript | November 8, 2026
Authornikmauro
Last UpdateNovember 8, 2026
LicenseMIT
Tags
Views159 views
Vanilla JS Country Dropdown with Flag Icons & Phone Codes – CountrySelect Pro

CountrySelect Pro is a vanilla JavaScript country selector that creates a searchable dropdown with SVG flags, ISO country codes, and international calling codes.

It supports custom country lists, localized names, keyboard navigation, Bootstrap 5 styling, and a form-associated Web Component.

You can use it with a text input, a native <select>, or <country-select>.

Features:

  • Filter countries by name, ISO code, phone code, or search alias.
  • Display SVG flags and preferred countries at the top of the list.
  • Load custom countries from arrays, inline JSON, or a URL.
  • Translate country names through Intl.DisplayNames.
  • Use native form validation and keyboard-accessible controls.
  • Open the dropdown above modal and overflow containers with the Popover API.
  • Customize Bootstrap 5 and plain CSS themes with CSS variables.
  • Initialize fields automatically after htmx updates and DOM changes.

Use Cases:

  • Choose shipping or billing countries in checkout forms.
  • Collect country codes in account registration and address forms.
  • Select international calling prefixes alongside a telephone field.
  • Enhance existing <select> fields while retaining their HTML options.

How To Use It:

1. Install CountrySelect Pro

Load the browser build from a CDN.

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/country-select.min.js" defer></script>

For a strict Content Security Policy that blocks injected styles, load the packaged CSS before the script. The dropdown arrow uses an SVG data: URL, which requires img-src data:; flags use jsDelivr unless you configure baseUrl.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/country-select.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/country-select.min.js" defer></script>

For npm projects, install the package and use its ES module build. The CSS import is optional when injected styles are permitted.

npm install country-select-pro
import CountrySelect from 'country-select-pro';
// Optional when using the packaged stylesheet:
import 'country-select-pro/css';

2. Create a Searchable Country Field

The country-select class enables automatic initialization. Set an initial ISO code through the input value.

<label for="country">Country</label>
<input id="country" class="country-select" name="country" value="GR" required>

Set data-value-type="phone" to store a calling prefix, or data-value-type="name" to store the country name.

<input class="country-select" name="dial_code" value="+30"
       data-value-type="phone" data-schema-return="{img} {phone}">

3. Initialize Manually

Pass an existing input, select element, or CSS selector to new CountrySelect(element, options). The optional second argument overrides the element’s data-* configuration and global defaults.

const picker = new CountrySelect('#country', {
  defaultCountry: 'GR',
  preferredCountries: ['GR', 'CY', 'DE'],
  lang: 'el'
});
picker.ready.then(() => console.log(picker.getCountry()));

4. Use the Web Component

The <country-select> custom element submits its selected value as a form field. It supports required, disabled, readonly, form reset, and associated labels.

<form>
  <label for="shipping-country">Shipping country</label>
  <country-select id="shipping-country" name="country" value="GR"
                  preferred="GR,CY" required></country-select>
  <button type="submit">Submit</button>
</form>

5. Enhance an Existing Select

An HTML <select> retains its own options before JavaScript runs. CountrySelect Pro enhances it when the script loads. Each option can also use data-code, data-phone, and data-flag to supply custom country details.

<select class="country-select" name="shipping_country" required>
  <option value="">Choose a country</option>
  <option value="GR">Greece</option>
  <option value="CY" selected>Cyprus</option>
  <option value="DE">Germany</option>
</select>

6. Load Custom Countries

Use data-countries for an inline JSON list or data-src for a JSON endpoint. The default merge: true fills missing details for recognized ISO codes.

<input class="country-select" data-countries='["GR","CY","IT"]'>
<input class="country-select" data-src="/api/shipping-countries.json">

The JavaScript countries option accepts an array, JSON string, URL, Promise, or function returning a list. Use setCountries() to replace the list later.

const picker = new CountrySelect('#country', {
  countries: () => fetch('/api/countries').then(response => response.json())
});
picker.ready.then(() => picker.setCountries(['GR', 'CY', 'IT']));

Supported list shapes include arrays of ISO codes or objects, {countries: [...]}, {items: [...]}, {data: [...]}, {results: [...]}, and maps such as {"GR": "Greece"}.

Custom item fieldMeaning
codeISO code. Aliases: iso2, iso, alpha2.
nameDisplayed label. Aliases: label, text.
phoneCalling code. Aliases: dial_code, dialCode.
flagFlag image URL or false.
valueCustom submitted value.
keywordsExtra searchable words, as a string or array.
aliasesAlias for keywords.

Configuration Options:

Set options through JavaScript, input data-* attributes, or window.CountrySelectConfig. Precedence is: constructor options, element attributes, global configuration, built-in defaults.

  • valueType (data-value-type): 'code' (default), 'phone', or 'name'. Determines the value saved in the form field.
  • schema (data-schema): Country list item template. Default {img} {name}; placeholders: {img}, {name}, {code}, {phone}.
  • schemaReturn (data-schema-return): Template for the selected value. Defaults to schema.
  • preferredCountries (data-preferred): Array or comma-separated ISO codes shown at the beginning of the dropdown.
  • onlyCountries (data-only-countries): Array or comma-separated ISO codes allowed in the list.
  • excludeCountries (data-exclude-countries): Array or comma-separated ISO codes omitted from the list.
  • defaultCountry (data-default-country): ISO code used when the field has no initial value.
  • placeholder (data-placeholder): Unselected-field text. English default: Select.
  • search (data-country-search): Boolean; enables the search input. Default true.
  • searchPlaceholder (data-search-placeholder): Search input text. English default: Search....
  • noResults (data-no-results): Message for an empty search. English default: Not found.
  • loading (JavaScript/global configuration): Loading message. English default: Loading....
  • loadError (JavaScript/global configuration): Error message for an unavailable list. English default: Could not load the list.
  • results (JavaScript/global configuration): Screen-reader plural result count. English default: {n} results.
  • result (JavaScript/global configuration): Screen-reader singular result count. English default: {n} result.
  • listLabel (JavaScript/global configuration): Accessible list name when the field has no label. English default: Countries.
  • rows (data-country-row-values): Number of visible dropdown rows. Default 5.
  • dropdownWidth (data-dropdown-width): CSS width for the dropdown, such as 300px. Default auto.
  • popover (data-popover): Boolean; uses the Popover API top layer when available. Default true; false keeps the list in-page.
  • lang (data-lang): Language for country names and UI text, such as el or de; auto reads the page language.
  • flags (data-flags): Boolean; shows flag icons. Default true.
  • sort (data-sort): Boolean; sorts countries by name. Default true; false preserves custom list order.
  • countries (data-countries): Custom country source. Attribute takes inline JSON; JavaScript accepts arrays, objects, strings, Promises, or functions.
  • dataUrl (data-src): URL for a JSON country list.
  • fetchOptions (JavaScript/global configuration): RequestInit options passed to fetch() for dataUrl.
  • merge (JavaScript/global configuration): Boolean; fills missing custom-country details from the built-in list. Default true.
  • baseUrl (data-base-url): Base folder for hosted flag assets. Defaults to this version of the package on jsDelivr.
  • flagPath (data-flag-path): Flag image URL template. Default {baseUrl}dist/flags/4x3/{code}.svg; use {code} for lowercase or {CODE} for uppercase.

Global Configuration

Declare global defaults before the browser script. The additional global options css and autoInit both default to enabled. Set either to false to disable injected CSS or automatic initialization.

<script>
window.CountrySelectConfig = {
  preferredCountries: ['US', 'CA'],
  lang: 'en',
  baseUrl: '/vendor/country-select-pro/'
};
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/country-select.min.js" defer></script>

Methods And Public Properties:

Constructor and Static API

Signature / propertyDescription
new CountrySelect(element, options?)Create or configure a picker for an input, select, or CSS selector. options is optional.
CountrySelect.init(root?)Initialize fields with .country-select inside a root element or document; default root is document.
CountrySelect.getInstance(element)Get the current picker instance for a selector, input, select, or <country-select>; returns null when absent.
CountrySelect.versionLibrary version.
CountrySelect.baseUrlDefault folder for flag assets.
CountrySelect.defaultsGlobal default configuration.
CountrySelect.ElementCustom element constructor, or null where unavailable.

Instance API

Method / propertyDescription
open()Open the dropdown.
close(refocus?)Close the dropdown; pass true to return focus to the control.
toggle()Toggle dropdown visibility.
setValue(value, {silent?})Select a value; dispatches change, input and country-change unless silent: true. Returns the instance.
getValue()Return the current form field value.
getCountry()Return {code, name, localName, phone, value} or null.
setCountries(source)Replace the country source and load the new list. Returns a Promise.
refresh()Synchronize the widget after changing input.value directly. Returns the instance.
destroy()Remove the widget and restore the original field; automatic initialization stops for that element until manually re-created.
readyPromise resolved when the initial country list has loaded.
inputThe original input or select element.
wrapperThe generated visible control element.
isOpenCurrent dropdown state.
const cs = CountrySelect.getInstance('#country');
if (cs) {
  cs.setValue('CY', {silent: true});
  console.log(cs.getValue(), cs.getCountry());
  cs.open();
}

Events:

country-change is a bubbling CustomEvent dispatched by the underlying field. Its detail contains value and country, where country is the selected country object or null. The picker also dispatches native change and input events.

document.querySelector('#country').addEventListener('country-change', event => {
  console.log(event.detail.value);
  console.log(event.detail.country?.phone);
});
// Native form event; compatible with htmx change triggers.
document.querySelector('#country').addEventListener('change', event => {
  console.log(event.target.value);
});

Web Component API:

The custom element accepts standard form attributes id, name, value, required, disabled, readonly, and accessible labels.

Custom element attributeJavaScript option
value-typevalueType
schemaschema
schema-returnschemaReturn
preferredpreferredCountries
only-countriesonlyCountries
exclude-countriesexcludeCountries
default-countrydefaultCountry
placeholderplaceholder
searchsearch
search-placeholdersearchPlaceholder
no-resultsnoResults
rowsrows
dropdown-widthdropdownWidth
popoverpopover
langlang
flagsflags
sortsort
countriescountries
srcdataUrl
base-urlbaseUrl
flag-pathflagPath

The custom element provides the following public properties and form methods.

Property / methodDescription
valueRead or set the submitted value. Assigning this property does not dispatch change events.
countryCurrent country object, or null.
instanceUnderlying CountrySelect instance, or null.
formAssociated form.
nameForm field name.
typeThe string country-select.
validityNative ValidityState.
validationMessageCurrent validation message.
willValidateWhether the field participates in constraint validation.
checkValidity()Check the field’s constraints.
reportValidity()Check constraints and report validation errors.

CSS Styling And Variables:

CountrySelect Pro follows Bootstrap 5 theme variables when present, including data-bs-theme="dark". On a plain CSS page, override the --cs-* variables on a parent or :root.

.shipping-form {
  --cs-bg: #fff;
  --cs-color: #222;
  --cs-border-color: #ccc;
  --cs-active-bg: #e07a2f;
  --cs-active-color: #fff;
  --cs-focus-border: #e07a2f;
  --cs-focus-ring: rgba(224, 122, 47, 0.3);
}
CSS variableControls
--cs-bgSelected-field background.
--cs-colorText color.
--cs-border-colorControl border.
--cs-radiusControl corner radius.
--cs-font-sizeControl font size.
--cs-mutedPlaceholder or muted text.
--cs-search-bgSearch field background.
--cs-dropdown-bgDropdown background.
--cs-dropdown-border-colorDropdown border.
--cs-divider-colorPreferred-country divider.
--cs-active-bgActive option background.
--cs-active-colorActive option text.
--cs-hover-bgHovered option background.
--cs-focus-borderFocused control border.
--cs-focus-ringFocused control shadow color.
--cs-invalidInvalid control color.
--cs-disabled-bgDisabled field background.
--cs-shadowDropdown shadow.
--cs-shadow-upDropdown shadow when opened upward.
--cs-z-indexStacking level for in-page dropdown.

Public CSS Classes

To supply your own styles, set window.CountrySelectConfig = {css: false} before loading the script. The following classes are used for the styled control.

ClassElement
.cs-wrapperOuter control.
.cs-triggerClickable selected-country trigger.
.cs-selected-contentSelected value and flag.
.cs-dropdownDropdown panel.
.cs-search-inputCountry search field.
.cs-listCountry list container.
.cs-optionList item.
.cs-option.activeActive keyboard/navigation item.
.cs-dividerDivider after preferred countries.

Alternatives:

Changelog:

v6.6.0 (11/08/2026)

  • Added the form-associated <country-select> Web Component and support for instance lookup from custom elements.

You Might Be Interested In:


Leave a Reply