Skip to Content
DocumentationGuidesCountry Dropdown

Country Dropdown Guide

Build accessible country and subdivision dropdowns using @koshmoney/countries.

React: Country Dropdown

A simple country selector that returns the ISO alpha-2 code.

import { useState } from 'react'; import { country } from '@koshmoney/countries'; function CountrySelect({ value, onChange }: { value: string; onChange: (code: string) => void; }) { const countries = country.all(); return ( <select value={value} onChange={(e) => onChange(e.target.value)}> <option value="">Select a country</option> {countries.map((c) => ( <option key={c.alpha2} value={c.alpha2}> {c.name} </option> ))} </select> ); }

React: Country + State Dropdown

A cascading dropdown where the state list updates based on the selected country.

import { useState } from 'react'; import { country, subdivision } from '@koshmoney/countries'; function AddressForm() { const [countryCode, setCountryCode] = useState(''); const [regionCode, setRegionCode] = useState(''); const countries = country.all(); const subdivisions = countryCode ? subdivision.forCountry(countryCode) : []; return ( <form> <label> Country <select value={countryCode} onChange={(e) => { setCountryCode(e.target.value); setRegionCode(''); // Reset state when country changes }} > <option value="">Select a country</option> {countries.map((c) => ( <option key={c.alpha2} value={c.alpha2}> {c.name} </option> ))} </select> </label> {subdivisions.length > 0 && ( <label> {subdivisions[0]?.type || 'State/Province'} <select value={regionCode} onChange={(e) => setRegionCode(e.target.value)} > <option value="">Select {subdivisions[0]?.type?.toLowerCase() || 'state'}</option> {subdivisions.map((s) => ( <option key={s.code} value={s.regionCode}> {s.name} </option> ))} </select> </label> )} </form> ); }

React: Country with Dial Code

A phone input with country dial code prefix.

import { useState } from 'react'; import { country } from '@koshmoney/countries'; import { dialCode } from '@koshmoney/countries/dialCode'; function PhoneInput() { const [countryCode, setCountryCode] = useState('US'); const [phone, setPhone] = useState(''); const countries = country.all(); const code = dialCode.getDialCode(countryCode); return ( <div style={{ display: 'flex', gap: '8px' }}> <select value={countryCode} onChange={(e) => setCountryCode(e.target.value)} > {countries.map((c) => { const dc = dialCode.getDialCode(c.alpha2); return ( <option key={c.alpha2} value={c.alpha2}> {c.name} ({dc}) </option> ); })} </select> <span>{code}</span> <input type="tel" value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="Phone number" /> </div> ); }

Vue: Country Dropdown

The same pattern works in Vue with v-model.

<script setup lang="ts"> import { ref, computed } from 'vue'; import { country, subdivision } from '@koshmoney/countries'; const countryCode = ref(''); const regionCode = ref(''); const countries = country.all(); const subdivisions = computed(() => countryCode.value ? subdivision.forCountry(countryCode.value) : [] ); function onCountryChange() { regionCode.value = ''; } </script> <template> <form> <label> Country <select v-model="countryCode" @change="onCountryChange"> <option value="">Select a country</option> <option v-for="c in countries" :key="c.alpha2" :value="c.alpha2" > {{ c.name }} </option> </select> </label> <label v-if="subdivisions.length > 0"> {{ subdivisions[0]?.type || 'State/Province' }} <select v-model="regionCode"> <option value="">Select {{ subdivisions[0]?.type?.toLowerCase() }}</option> <option v-for="s in subdivisions" :key="s.code" :value="s.regionCode" > {{ s.name }} </option> </select> </label> </form> </template>

Tree-Shaking for Specific Countries

If you only support a few countries and want to minimize bundle size, import subdivisions selectively:

import { country } from '@koshmoney/countries'; import '@koshmoney/countries/subdivision/US'; import '@koshmoney/countries/subdivision/CA'; import '@koshmoney/countries/subdivision/GB'; import { forCountry } from '@koshmoney/countries/subdivision'; // Only US, CA, and GB subdivisions are loaded (~5KB instead of ~55KB) const states = forCountry('US'); // Works const provinces = forCountry('CA'); // Works const regions = forCountry('DE'); // Returns [] (not imported)