Library / SDK
annexare/Countries avatar
annexare/Countries

countries-list: typed country, language, continent and currency data

Countries, Languages & Continents data (capital and currency, native name, calling codes).

1,332 stars425 forksTypeScriptMIT

At a glance

What is it?
countries-list is a TypeScript dataset of ISO 3166-1 countries, ISO 639-1 languages, continents and ISO 4217 currencies with utility functions for lookup, distributed on npm, Packagist and Bun. It suits teams that keep retyping country and currency data into forms, exports and payment code.
Who is it for?
countries-list is a good fit for a TypeScript or JavaScript team that needs country and currency reference data with autocomplete in the editor, and for PHP projects that want the same dataset from Packagist rather than maintaining a CSV.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 25 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • annexare/Countries1,332 ★ · -1
Star history
Repository2026-10-172026-11-07
annexare/Countries1.3k1.3k

-1 Stars in the last 21 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One dataset behind three package ecosystems

The repository publishes the same reference data through three different package managers, which is unusual and worth understanding before you pick one. The npm package is countries-list, the Packagist package is annexare/countries-list, and the Bun package name is also countries-list. The package is built as a Bun workspaces monorepo, with a root package named countries-workspace and a packages directory holding the individual packages, and the root package.json declares the workspace pattern as packages/* with a default catalog pinning @biomejs/biome 2.5.3, @types/bun 1.3.14 and typescript 7.0.2. The published JavaScript build sits in the dist directory of the repository, and the source data lives under packages/countries. For a PHP team the Packagist route means the same dataset arrives through Composer rather than through a Node toolchain, which matters more than it sounds, since it means the data can be refreshed by a PHP dependency bump instead of a JavaScript one. The repository also carries a composer.json at the root, so the PHP distribution is a first-class target rather than an afterthought.

What each country record actually contains

The field list is the reason to use this kind of package rather than a spreadsheet. Each country carries an ISO 3166-1 alpha-2 code, a mapping set from alpha-2 to alpha-3, a name, ISO 639-1 language codes, a capital, ISO 4217 currency codes with symbols and numeric codes, a native name, and calling codes. The README shows the shape as a typed object, and the Ukraine entry is a complete example:

ts
const countries = {
  // ...
  UA: {
    name: 'Ukraine',
    native: 'Україна',
    phone: [380],
    continent: 'EU',
    capital: 'Kyiv',
    currency: ['UAH'],
    languages: ['uk'],
  },
  // ...
}

Two details in that record are easy to miss and both cause real bugs when data is hand written. The currency and languages fields are arrays, not single values, because a country can have more than one. Calling codes are also arrays, and they are numbers rather than strings, so a country sharing a calling code with a neighbour is represented as two countries pointing at the same number. The continent field is a code into the continents map, not a name, which keeps the reference consistent.

The continents map itself is small and closed:

ts
const continents = {
  AF: 'Africa',
  AN: 'Antarctica',
  AS: 'Asia',
  EU: 'Europe',
  NA: 'North America',
  OC: 'Oceania',
  SA: 'South America',
}

Lookup functions resolve a name in any language

The exported utilities are the part of the API you will actually call. The module exports continents, countries, languages and a set of functions, and the documented examples show that a country can be resolved by its English name or by its native name, with getCountryCode returning the same alpha-2 code for both:

ts
getCountryCode('Ukraine') // 'UA'
getCountryCode('Україна') // 'UA'
getCountryData('UA') // ICountryData

That is a genuinely useful property for user-facing forms, where the user types whatever they know, and it is the sort of thing that gets reimplemented badly in every project that has a country dropdown. The other named utilities are getCountryData, getCountryDataList and getEmojiFlag. The list variant suggests bulk resolution, and the emoji flag helper covers the display case where you want a flag rather than a code. The language dataset carries the same pattern of English name, native name, and a right to left flag, with Urdu shown as the example of a language that needs it:

ts
const languages = {
  // ...
  uk: {
    name: 'Ukrainian',
    native: 'Українська',
  },
  ur: {
    name: 'Urdu',
    native: 'اردو',
    rtl: 1,
  },
  // ...
}

Currency data is opt-in because of bundle size

Version 3 moved the full currency dataset behind a subpath import, and the reason given is bundle size, which is a design decision worth understanding rather than a quirk. The subpath is countries-list/currencies, and it exposes the currencies collection plus getCurrency and getCurrencyByNumeric:

ts
import { currencies, getCurrency, getCurrencyByNumeric } from 'countries-list/currencies'

Each currency carries an English name, a native name, a UI symbol, a native symbol, a three digit numeric code and the number of minor unit decimals, with the Ukrainian hryvnia shown as a full record carrying the symbol and the numeric code 980 with two decimals. The numeric lookup is called out for banking use, where a wire format identifies a currency by number rather than by letter. Two rules about the data are stated explicitly. The dataset covers the complete active ISO 4217 list plus a few withdrawn codes that country data still references, and those withdrawn entries are flagged with withdrawn: true so you can filter them. And where a currency has no distinct Latin glyph, the symbol falls back to the ISO code, as with CHF and KWD, while the local glyph lives in symbolNative. The symbols come from Unicode CLDR, because ISO 4217 itself defines no symbols at all.

The dist directory is generated and the data has one source of truth

The contribution rules are unusually strict, and they explain how the data stays consistent. Everything is generated from strongly typed files in packages/countries/src, including the SQL file, and everything in the dist directory is generated. The instruction to contributors is that data related changes must be made only to files under packages/countries and then committed, and the build and test commands are bun run build and bun run test. The currency dataset is the one part with a stated external source and a named generator: it is produced from the official ISO 4217 list and Unicode CLDR by packages/scripts/generateCurrencies.ts, and the refresh command is cd packages/scripts && bun run generate:currencies, which the documentation notes requires network access. The intent is that a maintainer refreshes currencies when ISO publishes an update, rather than anyone editing currency records by hand. If you want to propose a data change, you are editing a TypeScript source file in packages/countries/src, not a JSON file, and not the published output.

ESM, CommonJS and IIFE in one package, plus minimal JSON maps

The build targets three module systems, and the documentation flags this as current rather than permanent, with the note that the JS build contains ES modules, CommonJS and IIFE for now. The file layout is explicit: CJS at cjs/index.js, ESM at mjs/index.js and IIFE at index.iife.js. The IIFE target is the one worth pausing on, because it is what lets the data be loaded in a browser without a bundler, and its presence here is a deliberate accommodation of script tag users rather than an accident. Alongside the main bundle there are minimal JSON maps that you can import directly for a single lookup, including the alpha-2 to alpha-3 and alpha-3 to alpha-2 maps and a native language names map. The minimal files exist so that a project that only needs a code mapping does not pull the whole dataset into its bundle. The lists are also published as JSON, CSV and SQL, which is the format to reach for when the consumer is a database rather than a JavaScript program.

Version 3 broke the old structure and the releases keep moving

If you are upgrading from an older release, the documentation is direct about it. Version 3 came with data structure changes and was completely reworked under the hood with TypeScript, ESM exports and a Bun workspaces monorepo layout, and everything is strongly typed so the data autocompletes in an IDE. The advice for projects that depended on the old structure is to carefully specify required versions in your dependencies, which is a warning worth heeding: a floating version range across the 2 to 3 boundary will break. The release history shows v3.4.1 on 2026-07-14, v3.4.0 on 2026-07-08 and v3.3.0 on 2026-03-04, and the root package.json carries version 3.4.1. The last push was on 2026-10-14, which is later than the latest release, so the branch is ahead of what npm serves and a fresh install will not necessarily match the tip. The licence field on the repository says MIT, and the project credits Annexare Studio with preparing the data from public sources.

Editorial conclusion

countries-list is a good fit for a TypeScript or JavaScript team that needs country and currency reference data with autocomplete in the editor, and for PHP projects that want the same dataset from Packagist rather than maintaining a CSV. It is the wrong choice for a project that needs authoritative reference data validated by a standards body, since the data is prepared from public sources and its ISO coverage is fixed at a point in time, and it is the wrong choice if you only need one or two countries, where a hardcoded constant is less work than a dependency. Before adopting it, check the fields you need against the documented set, since capital, calling codes and native names are present but currency data is an opt-in subpath import, and note that the current release line is 3.4.1 from 2026-07-14 while the last push was on 2026-10-14, so the branch can move ahead of the published package.

Frequently asked questions

How do I install countries-list?

The package is published for three ecosystems: bun add countries-list, npm install countries-list, and composer require annexare/countries-list for PHP projects. The repository itself is a Bun workspaces monorepo and uses bun run build and bun run test to build and test the generated files.

What data does each country record contain in countries-list?

Each country carries an ISO 3166-1 alpha-2 code with an alpha-2 to alpha-3 mapping set, a name, ISO 639-1 language codes, a capital, ISO 4217 currency codes with symbols and numeric codes, a native name, and calling codes. The currency and language fields are arrays, and calling codes are numbers.

Why are currencies imported from a separate path in countries-list?

The full ISO 4217 currency data is an opt-in import through the countries-list/currencies subpath so that it does not affect the main bundle size. That import exposes the currencies collection plus getCurrency and getCurrencyByNumeric for lookups by code or by three digit numeric code.

How do I look up a country by its native name in countries-list?

The getCountryCode function accepts either name form and returns the ISO 3166-1 alpha-2 code, with the documented example showing getCountryCode('Ukraine') and getCountryCode('Україна') both returning 'UA'. The full record for a code comes from getCountryData.

What changed in version 3 of countries-list?

Version 3 introduced data structure changes and was reworked under the hood with TypeScript, ESM exports and a Bun workspaces monorepo structure, with everything strongly typed for IDE autocompletion. Projects depending on the old structure are advised to specify required versions carefully in their dependencies.

Official sources

  1. annexare/Countries on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/annexare-countries.svg)](https://hysenlabs.com/projects/annexare-countries)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/annexare-countries.svg)](https://hysenlabs.com/projects/annexare-countries)

Was this useful?