Country (ISO code)
The input value is the ISO code (GR). Search ignores accents: try cote or curacao.
Lightweight, zero-dependency vanilla JavaScript country selector
Flags · phone codes · search · custom lists · accessible · Bootstrap 5 or plain CSS · htmx ready
Version 6.2.0 · MIT
Add the script, then the class country-select to any input or select.
<script src="https://cdn.jsdelivr.net/npm/country-select-pro@6.2.0/dist/country-select.min.js" defer></script>
<input type="text" class="country-select" name="country" value="GR">npm install country-select-pro
import CountrySelect from 'country-select-pro'; // ES module + TypeScript types
import 'country-select-pro/css'; // optional: CSS file (strict CSP)
new CountrySelect('#country', { preferredCountries: ['GR', 'CY'] });Every demo below is live; the code under it is exactly the HTML that runs. More standalone pages in docs/examples.
The input value is the ISO code (GR). Search ignores accents: try cote or curacao.
data-value-type="phone" stores +30. Search by number: type 357.
Shown first, with a divider. A shared phone code (+1) resolves to the main country.
Your own list in the HTML: works without JavaScript and is crawlable. Option texts are kept.
Codes only, or your own names / values. "flag": false for non-country items, data-sort="false" keeps your order.
Any JSON: [...], {countries: [...]}, {"GR": "Greece"}. Missing names / phone codes come from the built-in list.
data-lang translates country names with Intl.DisplayNames (any language). Search accepts both: ελλαδα or greece.
required works with native validation, Bootstrap's .was-validated and .is-invalid.
country-change (plus change / input) and methods on the instance.
Set --cs-* variables on any parent. Without Bootstrap the defaults are used.
States follow the input; data-country-search="false", data-flags="false".
Inside data-bs-theme="dark" colors follow Bootstrap automatically – no extra CSS.
The list opens in the browser's top layer (Popover API): this box has overflow: hidden, yet the list is fully visible. The same in Bootstrap modals / offcanvas and <dialog>.
A real form field: submitted with its name; required, reset, <label for> and disabled <fieldset> work natively. Press Submit with nothing selected.
More: htmx (swap, morph, history) · without any framework · JavaScript API · all examples
Change the settings – the preview and the code update live.
· country: As data-* attributes, JavaScript options (they win) or global defaults in window.CountrySelectConfig.
| Attribute | JS option | Default | Description |
|---|---|---|---|
data-value-type | valueType | code | Value written to the input: code (ISO), phone (+30) or name |
data-schema | schema | {img} {name} | Template of list items: {img} {name} {code} {phone} |
data-schema-return | schemaReturn | = schema | Template of the selected value |
data-preferred | preferredCountries | – | Codes shown first, e.g. GR,CY |
data-only-countries | onlyCountries | – | Show only these codes |
data-exclude-countries | excludeCountries | – | Hide these codes |
data-default-country | defaultCountry | – | ISO code selected when the input is empty |
data-placeholder | placeholder | Select | Text when nothing is selected (a <select> uses its empty option) |
data-country-search | search | true | Search box |
data-search-placeholder | searchPlaceholder | Search... | Search box placeholder / label |
data-no-results | noResults | Not found | Empty search text |
data-country-row-values | rows | 5 | Visible rows |
data-dropdown-width | dropdownWidth | auto | e.g. 300px |
data-popover | popover | true | List in the top layer (Popover API): not clipped by modals / offcanvas / overflow: hidden. false = inside the page |
data-lang | lang | – | Language of names and texts: el, de … or auto (<html lang>) |
data-flags | flags | true | false = no flags |
data-sort | sort | true | false = keep the order of your list |
data-countries | countries | – | Your list: JSON / array / function (may return a Promise) |
data-src | dataUrl | – | URL of your list (JSON) |
data-base-url | baseUrl | jsDelivr | Folder that contains dist/flags/ (self-hosting the flags; the country list is inside the script) |
data-flag-path | flagPath | {baseUrl}dist/flags/4x3/{code}.svg | Flag URL template ({code} = gr, {CODE} = GR) |
| – | fetchOptions | – | fetch() options for dataUrl |
| – | merge | true | Fill missing names / phone codes of your list from the built-in list |
window.CountrySelectConfig | – | Global defaults (any option) + css: false (no built-in CSS), autoInit: false | |
Elements with the class country-select are initialized automatically (also after htmx swaps); new CountrySelect(el | '#selector', options) for manual use.
CountrySelect.getInstance(el) | Instance of an element (or null) |
CountrySelect.init(root) | Initialize .country-select inside root |
cs.setValue(v, {silent}) | Select by value; sends events unless silent |
cs.getValue() | Current value |
cs.getCountry() | {code, name, localName, phone, value} or null |
cs.open() · close() · toggle() | Dropdown |
cs.setCountries(list) | New list: array, function, JSON, URL |
cs.refresh() | Re-read the value after input.value = … |
cs.destroy() | Remove the widget, restore the input |
cs.ready | Promise – resolved when the list is loaded |
input.addEventListener('country-change', (e) => {
e.detail.value; // "GR"
e.detail.country; // {code, name, localName, phone, value}
});
// change / input are dispatched too:
// <input class="country-select" hx-get="/zip" hx-trigger="change">| ↓ ↑ Enter Space | Open (closed) · move / select (open) |
| a letter | Open and search |
| Home End PgUp PgDn | First · last · page |
| Esc · Tab | Close (a surrounding modal stays open) · leave |
Bootstrap 5: nothing to do (light / dark). Anything else: CSS variables on any parent or :root.
| Variable | Falls back to | Used for |
|---|---|---|
--cs-bg | --bs-body-bg · #fff | Control and search background |
--cs-color | --bs-body-color · #212529 | Text and arrow |
--cs-border-color | --bs-border-color · #ced4da | Borders |
--cs-radius | --bs-border-radius · .375rem | Corners |
--cs-font-size | 1rem | Control text |
--cs-focus-border · --cs-focus-ring | #86b7fe · --bs-focus-ring-color | Focus |
--cs-hover-bg | --bs-tertiary-bg | Option hover |
--cs-active-bg · --cs-active-color | --bs-primary · #fff | Highlighted option |
--cs-muted | --bs-secondary-color | Placeholder, messages |
--cs-search-bg | --bs-tertiary-bg | Search area |
--cs-dropdown-bg · --cs-dropdown-border-color | --cs-bg · --bs-border-color-translucent | Dropdown |
--cs-divider-color | --bs-border-color | Divider after preferred |
--cs-invalid | --bs-form-invalid-border-color | Invalid state |
--cs-disabled-bg | --bs-secondary-bg | Disabled state |
--cs-shadow · --cs-z-index | – · 2050 | Dropdown shadow, stacking |