Tutorial

How to Build a Country Selector Dropdown with React

January 15, 2025 8 min read CountryDataAPI Team

Step-by-step guide to building a country selector dropdown in React using CountryDataAPI. Includes code examples with useState, useEffect, and Axios.

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.

Related Guides