@luwiostack/country
v0.1.0
Published
Typed ISO 3166 country data for React — countries, borders, dialing codes, currencies and spoken languages, with a `<Country>` provider and `useCountry` hook.
Readme
@luwiostack/country
Typed ISO 3166 country data for JavaScript — countries, land borders, dialing codes,
currencies and the languages spoken in each. A small, immutable domain model; framework-agnostic
(no React required). Continents live in their own package,
@luwiostack/continent — each country carries its continent_code.
Part of Luwio — standalone, with no dependency on any other
@luwiostack/* package. It pairs well with them (notably @luwiostack/language and
@luwiostack/locale), but the reverse: they depend on country, reading its plain
code fields, not the other way around.
Install
npm install @luwiostack/countryUsage
import { Country, Countries } from '@luwiostack/country'
const be = Country.new({ code: 'BE' }) // ISO 3166-1 alpha-2
be.machine_name // 'belgium' — stable, translation-safe key (no display name is exposed)
be.code // 'BE'
be.alpha3 // 'BEL'
be.numeric // '056'
be.dialing_code // '+32'
be.currency_code // 'EUR'
be.continent_code // 'EU' (resolve the object via @luwiostack/continent)
be.timezone_names // ['Europe/Brussels'] (resolve DST-aware Timezone objects via @luwiostack/timezone)
be.language_codes // ['nl', 'fr', 'de'] (resolve Language objects via @luwiostack/language)
be.borders().toArray().map((c) => c.code) // ['FR', 'DE', 'LU', 'NL']
// Country only carries the plain codes — resolve the rich objects with each sibling
// package's `.of()`, without @luwiostack/country depending on any of them:
import { Currency } from '@luwiostack/currency'
import { Languages } from '@luwiostack/language'
import { Timezones } from '@luwiostack/timezone'
Currency.of(be)?.symbol // '€' (or null, e.g. Antarctica)
Timezones.of(be).toArray().map((t) => t.name) // ['Europe/Brussels']
Languages.of(be).toArray().map((l) => l.machine_name) // ['dutch', 'french', 'german']
// Same new(), with a format for alpha-3 or numeric:
Country.new({ code: 'BEL', format: 'alpha3' }).code // 'BE'
// Collections:
Countries.benelux().toArray().map((c) => c.code) // ['BE', 'NL', 'LU']
Countries.eu().size // 27 — also eurozone(), schengen(), nordics(),
// baltics(), visegrad(), g7(), asean()
// Countries is an immutable, alpha-3-deduplicated collection — iterable, with the usual helpers:
for (const c of Countries.benelux()) c.code // 'BE', 'NL', 'LU'
Countries.benelux().has(be) // true
Countries.benelux().map((c) => c.code) // ['BE', 'NL', 'LU']
Countries.benelux().filter((c) => c.code !== 'LU').size // 2
Countries.empty().isEmpty() // trueAn unknown code throws — Country.new({ code: 'ZZ' }) → Error: Unknown country: ZZ.
A country with no official currency (e.g. Antarctica) exposes currency_code === NO_CURRENCY_CODE
('NONE') and Currency.of(country) returns null — the empty string is never used, so
Countries.usingCurrency({ code: '' }) is an empty collection.
API surface
- Domain:
Country(.new,.tryNew),Countries - Utils:
toMachineName - Types:
ICountry,ICountries,CountryCodeFormat,NO_CURRENCY_CODE
Continents moved to @luwiostack/continent — Continent, Continents,
CONTINENT_MAP, IContinent. Go from a country to its continent with
Continent.of(country).
Data
The bundled ISO 3166 dataset is generated from @luwiostack/iso-data (the monorepo's source of
truth) and ships inside this package — nothing is fetched or read at runtime. Spoken languages
are stored as ISO 639-1 codes and resolved to Language objects via @luwiostack/language.
Flags
Ready-to-use flag SVGs ship in this package's flags/ folder, one file per country named
by its lowercase alpha-2 code (be.svg, nl.svg, us.svg, …) — matching
country.alpha2.toLowerCase(). Like the translation catalogs, they're excluded from the
published bundle (files: ["dist"]), so they add nothing to an install: download the ones you
need from the docs site, host them on your own CDN, and build the URL yourself:
const flagUrl = (country: ICountry) =>
`https://cdn.example.com/flags/${country.alpha2.toLowerCase()}.svg`See flags/README.md for the source and licensing note.
