FlagsCountries APIHTMLCSSTutorial

Country Flags API: Display Country Flags in HTML and CSS

ApogeoAPI5 min read

Three ways to display country flags

  1. CSS emoji flags — browser-rendered, no images, works everywhere modern
  2. SVG flag CDN — crisp at any size, accessible via country code
  3. ApogeoAPI flagUrl field — get the CDN URL directly from the countries API

Method 1: CSS emoji flags

Convert a 2-letter ISO country code to a flag emoji using Unicode regional indicator symbols:

function countryCodeToEmoji(countryCode) {
  // Each letter maps to a Regional Indicator Symbol (U+1F1E6 = 🇦)
  return countryCode
    .toUpperCase()
    .split('')
    .map(char => String.fromCodePoint(char.charCodeAt(0) + 127397))
    .join('');
}

countryCodeToEmoji('US'); // 🇺🇸
countryCodeToEmoji('DE'); // 🇩🇪
countryCodeToEmoji('BR'); // 🇧🇷
countryCodeToEmoji('JP'); // 🇯🇵

Render in HTML with accessible aria-label:

// React component
function Flag({ countryCode, countryName }) {
  const emoji = countryCode
    .toUpperCase()
    .split('')
    .map(c => String.fromCodePoint(c.charCodeAt(0) + 127397))
    .join('');

  return (
    <span
      role="img"
      aria-label={`Flag of ${countryName}`}
      className="text-2xl"
    >
      {emoji}
    </span>
  );
}

// Usage
<Flag countryCode="US" countryName="United States" />  // 🇺🇸

Note: Windows renders emoji flags as 2-letter text (US, DE) rather than flag images. If Windows support matters, use SVG instead.

Method 2: SVG flag CDN (flagcdn.com)

flagcdn.com serves SVG flags for every ISO 3166-1 alpha-2 country code — free, no API key:

<!-- Format: https://flagcdn.com/{lowercase-iso2}.svg -->
<img
  src="https://flagcdn.com/us.svg"
  alt="United States flag"
  width="32"
  height="24"
  loading="lazy"
/>
function getFlagUrl(countryCode, width = 32) {
  // flagcdn.com supports width-based PNG: /w{width}/{code}.png
  // or SVG: /{code}.svg
  const code = countryCode.toLowerCase();
  return `https://flagcdn.com/${code}.svg`;
  // For PNG with specific width: `https://flagcdn.com/w${width}/${code}.png`
}

getFlagUrl('US');  // "https://flagcdn.com/us.svg"
getFlagUrl('DE');  // "https://flagcdn.com/de.svg"

Method 3: ApogeoAPI countries endpoint

ApogeoAPI returns a flagUrl field directly from the countries endpoint:

GET https://api.apogeoapi.com/v1/countries/US?apikey=YOUR_KEY

# Response includes:
{
  "iso2": "US",
  "commonName": "United States",
  "flagUrl": "https://flagcdn.com/us.svg",
  "currencyCode": "USD",
  "dialCode": "+1",
  ...
}
async function getCountryWithFlag(countryCode) {
  const res = await fetch(
    `https://api.apogeoapi.com/v1/countries/${countryCode}?apikey=${process.env.APOGEO_KEY}`
  );
  const country = await res.json();
  return {
    name: country.commonName,
    flag: country.flagUrl,
    currency: country.currencyCode,
    dialCode: country.dialCode,
  };
}

const us = await getCountryWithFlag('US');
// { name: "United States", flag: "https://flagcdn.com/us.svg",
//   currency: "USD", dialCode: "+1" }

Detect flag from visitor's IP

Combine IP geolocation with the flag URL to show a visitor's flag in real time:

// One call gets country code — derive flag URL locally (no second call)
async function getVisitorFlag(): Promise<{ flag: string; countryName: string }> {
  const res = await fetch(
    `https://api.apogeoapi.com/v1/geo/self?apikey=${process.env.NEXT_PUBLIC_APOGEO_KEY}`
  );
  const geo = await res.json();

  const flagUrl = `https://flagcdn.com/${geo.countryCode.toLowerCase()}.svg`;

  return { flag: flagUrl, countryName: geo.countryName };
}

// React usage
function VisitorFlag() {
  const [flag, setFlag] = useState<string | null>(null);

  useEffect(() => {
    getVisitorFlag().then(({ flag }) => setFlag(flag));
  }, []);

  return flag ? (
    <img src={flag} alt="Your country" width={24} height={18} />
  ) : null;
}

Country selector with flags

// Full country list with flags from ApogeoAPI
async function getAllCountriesWithFlags() {
  const res = await fetch(
    `https://api.apogeoapi.com/v1/countries?apikey=${process.env.APOGEO_KEY}`
  );
  const countries = await res.json();
  return countries.map((c: any) => ({
    code: c.iso2,
    name: c.commonName,
    flag: c.flagUrl ?? `https://flagcdn.com/${c.iso2.toLowerCase()}.svg`,
    dialCode: c.dialCode,
  }));
}

// React select component
function CountrySelect({ onChange }) {
  const [countries, setCountries] = useState([]);

  useEffect(() => {
    getAllCountriesWithFlags().then(setCountries);
  }, []);

  return (
    <select onChange={e => onChange(e.target.value)} className="border rounded px-2 py-1">
      {countries.map(c => (
        <option key={c.code} value={c.code}>
          {/* Emoji flag + country name */}
          {countryCodeToEmoji(c.code)} {c.name}
        </option>
      ))}
    </select>
  );
}

Performance tips

  • Lazy-load flag images: Use loading="lazy" on <img> tags — most visitors don't scroll to see every flag in a list
  • Cache country list: The full countries list changes rarely. Cache it in localStorage with a 24h TTL
  • Prefer emoji for single flag display: For showing just the visitor's country, emoji is 0-byte network overhead vs an SVG fetch
  • Use CSS filter for greyscale: filter: grayscale(100%) on inactive flags in a selector looks professional and reduces visual noise

Full API docs at api.apogeoapi.com/api/docs. Free tier at app.apogeoapi.com/register.

Try ApogeoAPI free

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

Get your free API key