A tilted grid of rounded tiles showing two-letter ISO 3166-1 alpha-2 country codes, with a few highlighted in color
Rushan

ISO 3166-1 Alpha-2 Country Codes: A Developer's Guide

  • iso-3166
  • country-codes
  • data-modeling
  • geoicons

Any application that ships across borders needs a way to name a country. You reach for a two-letter code, write US, JP, DE, and move on.

Then a support ticket arrives. A user in Belfast picked "United Kingdom" and your shipping API rejected UK. Someone in Pristina found no option at all. Your analytics dashboard shows a country called AN that dissolved in 2010.

These bugs share one root: ISO 3166-1 alpha-2 carries more rules than its two characters suggest. Let's walk through the parts that break real applications, and how to model country data so the next revision of the standard does not break yours.

Key takeaways

  • ISO 3166-1 defines 249 officially assigned alpha-2 codes. UK is not one of them. The United Kingdom is GB.
  • Four other status categories exist: user-assigned, exceptionally reserved, transitionally reserved, and indeterminately reserved. They follow different rules.
  • Kosovo uses XK, a code from the user-assigned range that ISO has never officially assigned.
  • Codes get recycled. CS meant Czechoslovakia, then Serbia and Montenegro.
  • Country names change far more often than their codes. Store the code, resolve the name at render time.

What alpha-2 covers

ISO 3166 splits into three parts. Part 1 names countries and their dependent territories. Part 2 names subdivisions inside them. Part 3 records codes that fell out of use.

Part 1 gives you three code sets for the same entity:

FormatJapanNotes
Alpha-2JPTwo letters. Used by ccTLDs, BCP 47 language tags, payment APIs.
Alpha-3JPNThree letters. Easier to read on its own.
Numeric-3392Digits from UN M49. Script-independent, survives alphabet changes.

Alpha-2 is the set you meet most often. Two characters fit anywhere, and the Internet Assigned Numbers Authority (IANA) draws the country-code top-level domains straight from the alpha-2 list, which puts these codes in front of everyone who ever registered a domain. That reach explains the misuse.

Five kinds of code

The 249 official codes get the attention. Most bugs come from the reserved ranges.

StatusExamplesWhat you do with them
Officially assignedUS, JP, DEUse these. 249 of them.
Exceptionally reservedEU, UK, UN, ACReserved for a specific requester. Never repurpose them.
Transitionally reservedAN, CS, YU, BURetired countries. Keep them readable in old records, block them in new input.
Indeterminately reservedRI, WG, FLVehicle registration codes that clash with ISO. Ignore.
User-assignedAA, QMQZ, XAXZ, ZZYours to use. ISO promises never to assign them.

That last row deserves a bookmark. When you need a code for something ISO does not cover, an internal test region or a disputed territory, take one from the user-assigned range. Inventing a code outside that range, ZQ or OJ, guarantees a collision the day ISO assigns it.

The United Kingdom is GB

This is the mistake that reaches production most often. The ISO code for the United Kingdom of Great Britain and Northern Ireland is GB.

UK exists in the standard, but only as an exceptional reservation. The reservation exists to accommodate .uk, which was already in wide use before ISO codes reached domain names, so the United Kingdom asked ISO to set the code aside rather than assign it elsewhere. It never became a country code, and a strict validator rejects it.

The confusion has an official source. The European Commission publishes country codes that match ISO with two deliberate exceptions: it writes UK for the United Kingdom and EL for Greece. If your data arrives from an EU statistical feed, normalize both before you store anything:

const EU_TO_ISO: Record<string, string> = { UK: 'GB', EL: 'GR' };

export function toIso3166(code: string): string {
  const upper = code.trim().toUpperCase();
  return EU_TO_ISO[upper] ?? upper;
}

Run every inbound code through one function like this. Patch each call site instead and the rules drift apart within a quarter.

Kosovo sits outside the standard

Kosovo has no officially assigned alpha-2 code. ISO assigns codes to entities that are UN members or appear on the UN Statistics Division's country list, and Kosovo's status blocks both routes.

In practice, XK won. The European Commission, the IMF, and SWIFT all use it, and it comes from the user-assigned XAXZ range, so ISO will never hand it to another country. GeoIcons ships the shape as Xk for the same reason.

import { Xk } from '@geoicons/react/countries';

<Xk size={64} aria-label="Kosovo" />
Demo

If your validator checks incoming codes against a strict ISO list, XK fails and Kosovo disappears from your form. Add it to the allowlist and document why.

Codes get recycled

A retired code moves to transitionally reserved status, where ISO intends to hold it for at least 50 years before it returns to circulation. Intends, not guarantees.

CS made the round trip in ten. It belonged to Czechoslovakia until 1993, and in 2003 it came back as Serbia and Montenegro. That union dissolved in 2006, so CS sits reserved again while RS and ME carry the successor states.

The Netherlands Antilles produced a cleaner split. When AN dissolved in October 2010, three codes replaced it: CW for Curaçao, SX for Sint Maarten, and BQ for Bonaire, Sint Eustatius and Saba.

Two consequences for your schema. A country code identifies a country at a point in time, so an old row carrying AN needs a date to interpret it. And a code you resolve to a name today may resolve to a different name in a decade, which rules out caching the pair as if it were permanent.

Names change, codes hold

The code is the stable half of the pair, and the name is the half that moves.

YearChangeCode
2013Cape Verde becomes Cabo VerdeCV
2016Czech Republic adds the short name CzechiaCZ
2018Swaziland becomes EswatiniSZ
2019Macedonia becomes North MacedoniaMK
2022Turkey becomes TürkiyeTR

Five renames in a decade, zero code changes. Store TR in your database and resolve the display name from a library you can update, such as i18n-iso-countries. Store "Turkey" as a string and you own a migration.

Search deserves the same treatment. A user who learned "Swaziland" in school will type it, so keep the old names as aliases even after the label changes.

Subdivisions live in part 2

England, Scotland, Wales, and Northern Ireland have no alpha-2 codes. They are subdivisions of GB under ISO 3166-2, written GB-ENG, GB-SCT, GB-WLS, and GB-NIR. US states follow the same pattern: California is US-CA.

Users care about this distinction more than standards bodies do. A Scottish customer filling in a shipping form wants to see Scotland. Your payment processor wants GB.

Handle both. Show the subdivision in the interface, send the country code to the API:

import { Scotland } from '@geoicons/react/countries';

const REGION = { id: 'GB-SCT', label: 'Scotland', country: 'GB' };

<button className="flex items-center gap-2" value={REGION.country} data-region={REGION.id}>
  <Scotland size={20} aria-hidden="true" />
  <span>{REGION.label}</span>
</button>

GeoIcons ships these four as England, Scotland, Wales, and NorthernIreland rather than forcing them into a code the standard does not define.

Regions have no country code at all

The European Union is not a country, so it holds EU as an exceptional reservation. ASEAN, Mercosur, and the GCC hold nothing. Neither does a shipping zone you invented last week.

Trade blocs and continents belong in a separate namespace from countries. Mixing an EU row into a table of ISO codes means every consumer of that table now needs to know which rows are real countries.

GeoIcons keeps the same separation. Countries import from @geoicons/react/countries, regions from @geoicons/react/areas:

import { UnitedKingdom, EuropeanUnionEu } from '@geoicons/react/areas';

<UnitedKingdom size={32} aria-label="United Kingdom" />
<EuropeanUnionEu size={32} aria-label="European Union" />
Demo

The United Kingdom is the one entity that does not ship as a country shape. There is no Gb export. The four constituents live in countries as England, Scotland, Wales, and NorthernIreland, and the union itself ships as an area shape, UnitedKingdom, alongside GreatBritain.

Rules for your codebase

Six habits that prevent most country-data bugs:

  1. Store the alpha-2 code, not the name. CHAR(2) is enough. Resolve names through a library at render time.
  2. Uppercase on the way in. The standard writes codes in uppercase. Lowercase shows up in domains and URLs, so normalize once at the boundary and compare case-insensitively.
  3. Validate against a real list. A regex of ^[A-Z]{2}$ accepts ZQ. Check membership in the 249 official codes plus the exceptions you choose to allow.
  4. Keep an explicit exception list. XK for Kosovo, EL and UK from EU feeds. Write the reasons in a comment next to the list.
  5. Do not derive flag emoji from codes without a fallback. Regional indicator pairs produce nothing for UK, XK, or any retired code, and Windows renders the letters instead of a graphic for every flag. Vector map outlines dodge both problems, which we covered in map shapes versus flag emojis.
  6. Version your reference data. Note which revision of the ISO list your snapshot came from, so a diff against next year's list takes minutes.

How GeoIcons maps to the standard

The library carries 255 country shapes. Most of them use the alpha-2 code in PascalCase as the export name, so US becomes Us and JP becomes Jp. The rest follow the edge cases described above:

  • UK constituents ship under their names: England, Scotland, Wales, NorthernIreland.
  • Kosovo ships as Xk, matching the code the financial world settled on.
  • The Caribbean Netherlands splits by island, since BQ covers three: BqBonaire and BqSintEustatius ship today.
  • Mainland variants drop distant territories for selectors that need a compact shape: FrMainland, AuMainland.

Every shape is a separate named export, so importing Us and Jp pulls two icons into your bundle and leaves the other 253 behind. Read why an icon library doesn't have to bloat your bundle for how that works.

FAQ

How many ISO 3166-1 alpha-2 codes are there?
249 codes are officially assigned to countries and dependent territories. Additional codes exist as user-assigned, exceptionally reserved, transitionally reserved, or indeterminately reserved entries, but none of those identify a current country.
Is the country code for the United Kingdom GB or UK?
GB. The code UK is exceptionally reserved because the .uk domain was already established when ISO codes reached the internet, and it is never an officially assigned country code. The European Commission uses UK in its own publications, which is why the wrong value spreads through EU data feeds.
What is Kosovo's ISO country code?
Kosovo has no officially assigned code. XK, drawn from the user-assigned XA to XZ range, is the de facto standard used by the European Commission, the IMF, and SWIFT. ISO will never assign XK to another country, so it is safe to adopt.
Should I store alpha-2 or alpha-3 codes?
Alpha-2 for most applications, since it matches ccTLDs, BCP 47 language tags, and the majority of payment and shipping APIs. Choose numeric-3 when your data outlives alphabet changes or crosses scripts, because those codes carry no Latin letters.
Do ISO country codes ever change?
Codes change far less often than names. Türkiye, Eswatini, and North Macedonia all renamed themselves without changing TR, SZ, or MK. Codes do get retired when a country dissolves. ISO aims to hold a retired code for at least 50 years before reassigning it, but it has broken that guideline: CS moved from Czechoslovakia to Serbia and Montenegro after only ten.

Get started

Install GeoIcons and render any country by its ISO code:

npm i @geoicons/react
import { Gr, Tr, Xk } from '@geoicons/react/countries';

export default function Balkans() {
  return (
    <div className="flex items-center gap-3 text-slate-900 dark:text-slate-100">
      <Gr size={24} aria-label="Greece" />
      <Tr size={24} aria-label="Türkiye" />
      <Xk size={24} aria-label="Kosovo" />
    </div>
  );
}

Browse the full set of 255 country shapes and 167 area shapes in the icon catalog, or read the API reference for sizing, color, and accessibility props.