ReactTutorialCountries

How to Build a Country Selector in React with ApogeoAPI

ApogeoAPI6 min read

Country selectors are one of the most common UI components — shipping forms, user profiles, tax settings. Here's how to build a production-ready one with flags and search using ApogeoAPI.

What We're Building

A searchable dropdown that:

  • Shows a flag emoji + country name for each option
  • Filters as the user types
  • Returns the selected country's ISO code
  • Uses TypeScript throughout

Step 1: Get Your Free API Key

Sign up at app.apogeoapi.com — the free tier includes countries + states forever. No credit card needed.

Step 2: Fetch the Countries

// types.ts
export interface Country {
  iso2: string;
  name: string;
  flag_url: string;
  region: string;
}

// useCountries.ts
import { useEffect, useState } from 'react';

export function useCountries() {
  const [countries, setCountries] = useState<Country[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://api.apogeoapi.com/v1/countries', {
      headers: { 'X-API-Key': process.env.NEXT_PUBLIC_APOGEO_API_KEY! }
    })
      .then(r => r.json())
      .then(data => setCountries(data))
      .finally(() => setLoading(false));
  }, []);

  return { countries, loading };
}

Step 3: Build the Selector Component

// CountrySelector.tsx
'use client';
import { useState } from 'react';
import { useCountries } from './useCountries';

interface Props {
  value: string;
  onChange: (isoCode: string) => void;
}

export function CountrySelector({ value, onChange }: Props) {
  const { countries, loading } = useCountries();
  const [search, setSearch] = useState('');

  const filtered = countries.filter(c =>
    c.name.toLowerCase().includes(search.toLowerCase())
  );

  if (loading) return <div>Loading countries...</div>;

  return (
    <div className="relative">
      <input
        type="text"
        placeholder="Search countries..."
        value={search}
        onChange={e => setSearch(e.target.value)}
        className="w-full border rounded px-3 py-2"
      />
      <ul className="absolute z-10 w-full bg-white border rounded max-h-60 overflow-y-auto">
        {filtered.map(country => (
          <li
            key={country.iso2}
            onClick={() => onChange(country.iso2)}
            className={`flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-gray-100 ${
              value === country.iso2 ? 'bg-blue-50' : ''
            }`}
          >
            <img src={country.flag_url} alt="" className="w-5 h-4 object-cover rounded-sm" />
            <span>{country.name}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

Step 4: Use It in Your Form

import { CountrySelector } from './CountrySelector';

function ShippingForm() {
  const [country, setCountry] = useState('');

  return (
    <form>
      <label>Country</label>
      <CountrySelector value={country} onChange={setCountry} />
    </form>
  );
}

Enhancements

  • Cache the response — countries don't change often. Use SWR or React Query with a long staleTime.
  • Add states/provinces — once a country is selected, fetch GET /v1/countries/{iso2}/states for a cascading state selector.
  • Phone codes — ApogeoAPI returns phone_code per country — useful for phone number inputs.

What's Next

With the same API key, you can add city autocomplete (GET /v1/countries/{iso2}/cities), detect the user's country from IP (GET /v1/geolocate/{ip}), and display live prices in their local currency — all in one subscription.

Try ApogeoAPI free

1,000 requests/month forever. 14-day full-access trial. No credit card.

Get your free API key