Building a country selector dropdown is one of the most common tasks in web development. Whether you're creating a registration form, a shipping address widget, or a localization feature, you need a reliable, up-to-date list of countries. In this tutorial, we'll walk through building a fully functional country selector using React and CountryDataAPI.
Prerequisites
Before we start, make sure you have:
- Node.js 18+ installed
- A React project (create-react-app or Vite)
- An API key from CountryDataAPI
- Axios installed (
npm install axios)
Step 1: Get Your API Key
Register at countrydataapi.com/register to get your API key. Plans start at $9.99/month — see the pricing page for full details.
Step 2: Set Up Your Component
Create a new file called CountrySelector.jsx:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const CountrySelector = ({ onCountryChange }) => {
const [countries, setCountries] = useState([]);
const [selectedCountry, setSelectedCountry] = useState('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchCountries = async () => {
try {
const response = await axios.get(
'https://api.countrydataapi.com/v1/countries/all',
{
headers: {
'x-api-key': process.env.REACT_APP_COUNTRY_API_KEY
}
}
);
// Sort alphabetically by name
const sorted = response.data.sort((a, b) =>
a.name.localeCompare(b.name)
);
setCountries(sorted);
} catch (err) {
setError('Failed to load countries. Please try again.');
console.error('Country API error:', err);
} finally {
setLoading(false);
}
};
fetchCountries();
}, []);
const handleChange = (e) => {
const value = e.target.value;
setSelectedCountry(value);
if (onCountryChange) {
const country = countries.find(c => c.iso2 === value);
onCountryChange(country);
}
};
if (loading) return <p>Loading countries...</p>;
if (error) return <p className="error">{error}</p>;
return (
<div className="country-selector">
<label htmlFor="country-select">Select Country</label>
<select
id="country-select"
value={selectedCountry}
onChange={handleChange}
aria-label="Country selector"
>
<option value="">-- Choose a country --</option>
{countries.map((country) => (
<option key={country.iso2} value={country.iso2}>
{country.name}
</option>
))}
</select>
</div>
);
};
export default CountrySelector;
Step 3: Understand the API Response
The /v1/countries/all endpoint returns an array of country objects. Each object includes:
name— Full country name (e.g., "United States")iso2— ISO 3166-1 alpha-2 code (e.g., "US")iso3— ISO 3166-1 alpha-3 code (e.g., "USA")phone_code— International dialing code (e.g., "+1")currency— Currency code (e.g., "USD")region— Geographic region (e.g., "Americas")flag— Emoji flag (e.g., "🇺🇸")
Step 4: Add Environment Variables
Create a .env file at the root of your React project:
REACT_APP_COUNTRY_API_KEY=your_api_key_here
Never commit your API key to version control. Add .env to your .gitignore file.
Step 5: Use the Component
Now use the CountrySelector component in a form:
import React, { useState } from 'react';
import CountrySelector from './CountrySelector';
const RegistrationForm = () => {
const [selectedCountry, setSelectedCountry] = useState(null);
return (
<form>
<h2>Registration</h2>
<CountrySelector onCountryChange={setSelectedCountry} />
{selectedCountry && (
<p>
Selected: {selectedCountry.flag} {selectedCountry.name}
(Dial: {selectedCountry.phone_code})
</p>
)}
<button type="submit">Register</button>
</form>
);
};
export default RegistrationForm;
Step 6: Add Loading and Error States
Good UX requires handling loading and error states properly. The example above already does this, but you can enhance it with a skeleton loader or a spinner:
if (loading) {
return (
<div className="skeleton-select">
<div className="skeleton-bar" style={{ width: '100%', height: '40px' }} />
</div>
);
}
Performance Tip: Cache the Response
Since the country list rarely changes, cache the result in localStorage to avoid making unnecessary API calls:
const CACHE_KEY = 'countrydata_countries';
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
const getCachedCountries = () => {
const cached = localStorage.getItem(CACHE_KEY);
if (!cached) return null;
const { data, timestamp } = JSON.parse(cached);
if (Date.now() - timestamp > CACHE_TTL) return null;
return data;
};
const setCachedCountries = (data) => {
localStorage.setItem(CACHE_KEY, JSON.stringify({
data,
timestamp: Date.now()
}));
};
Next Steps
Now that you have a working country selector, you can extend it by:
- Adding a state/province dropdown that loads dynamically based on the selected country
- Showing country flags using the emoji field
- Filtering countries by region (e.g., only show European countries)
- Adding phone code prefixes for international phone number inputs
Check out our States by Country endpoint and Cities by State endpoint to build a full cascading address form.

