Skip to Content

Introducing @koshmoney/countries

We built @koshmoney/countries because working with country data in JavaScript should not require five different npm packages, manual TypeScript types, and a prayer that your bundle size stays reasonable.

The Problem

If you have ever built a form with a country dropdown, an address validator, or a compliance check, you know the pain. The npm ecosystem for country data is fragmented:

  • iso-3166-1 gives you country codes but not subdivisions
  • iso-3166-2 gives you subdivisions but has not been updated since 2017
  • countries-list has country names and currencies but no subdivision data
  • country-state-city has subdivisions and cities but ships a massive bundle and uses a GPL license
  • None of them validate postal codes, provide currency symbols, or check EU/SEPA membership

You end up installing multiple packages, writing glue code, creating your own TypeScript interfaces, and hoping the data stays consistent across packages.

What We Built

@koshmoney/countries is a single, unified library that covers all the country data most applications need:

import { country, subdivision, postalCode } from '@koshmoney/countries'; // Country lookups -- alpha-2, alpha-3, numeric, name country.whereAlpha2('US'); // { name: 'United States', alpha2: 'US', alpha3: 'USA', numeric: '840' } // 5,000+ subdivisions -- states, provinces, regions subdivision.whereCode('US-CA'); // { code: 'US-CA', name: 'California', type: 'State', ... } // Postal code validation for 150+ countries postalCode.isValid('US', '90210'); // true postalCode.isValid('GB', 'SW1A 1AA'); // true postalCode.getName('US'); // 'ZIP Code'

Specialized data is available through subpath imports, so you only pay for what you use:

import { currency } from '@koshmoney/countries/currency'; currency.getCurrency('US'); // { code: 'USD', symbol: '$', name: 'US Dollar' } import { membership } from '@koshmoney/countries/membership'; membership.isEU('FR'); // true membership.isSEPA('CH'); // true import { geography } from '@koshmoney/countries/geography'; geography.getContinent('JP'); // 'Asia' import { dialCode } from '@koshmoney/countries/dialCode'; dialCode.getDialCode('GB'); // '+44'

Key Design Decisions

TypeScript-First

Every function is fully typed. Return types, parameter types, and the data structures themselves all have proper TypeScript definitions. No @types/ package needed, no any escape hatches.

import type { Country, Subdivision, CurrencyInfo } from '@koshmoney/countries'; const us: Country | null = country.whereAlpha2('US'); const ca: Subdivision | null = subdivision.whereCode('US-CA');

Tree-Shakeable Architecture

The library separates core modules (country, subdivision, postal codes) from specialized modules (currency, dial codes, geography, membership). Core modules ship in the main bundle. Specialized modules use subpath exports  so bundlers only include the data you actually import.

Need just country codes? Import only the country module:

import { whereAlpha2, isValid } from '@koshmoney/countries/country';

Need only US and Canadian subdivisions? Import just those:

import '@koshmoney/countries/subdivision/US'; import '@koshmoney/countries/subdivision/CA'; import { whereCode } from '@koshmoney/countries/subdivision';

For full details, see the tree-shaking guide.

Consistent API Patterns

Every module follows the same naming conventions so you can predict function names without checking docs:

PatternPurposeExample
where*Look up by criteriawhereAlpha2('US')
to*Convert between formatsalpha2ToAlpha3('US')
is*Boolean validationisValid('US')
for*Get collectionforCountry('US')
get*Get specific valuegetCurrency('US')

Automatic Input Normalization

All functions handle case normalization internally. Pass 'us', 'Us', or 'US' and get the same result. No need to uppercase input yourself.

Null-Safe Returns

Lookup functions return null for invalid inputs instead of throwing. This makes them safe to use in pipelines and optional chains:

const name = country.whereAlpha2(userInput)?.name ?? 'Unknown';

Getting Started

Install the package:

npm install @koshmoney/countries

Use it in your code:

import { country, subdivision, postalCode } from '@koshmoney/countries'; // Build a country dropdown const allCountries = country.all(); // Get states for the selected country const states = subdivision.forCountry('US'); // Validate a ZIP code const isValidZip = postalCode.isValid('US', '90210');

Check out the installation guide and getting started tutorial for more detailed setup instructions.

What Sets Us Apart

Feature@koshmoney/countriesiso-3166-1countries-listcountry-state-city
Country codesYesYesYesYes
Subdivisions5,000+NoNoYes
Postal code validation150+ countriesNoNoNo
Currency dataYesNoYesNo
Dial codesYesNoYesNo
EU/SEPA membershipYesNoNoNo
TypeScriptBuilt-inCommunityBuilt-inBuilt-in
Tree-shakeableYesNoNoNo
LicenseMITMITMITGPL-3.0
Dependencies0 (core)000

For a detailed comparison with code examples, see our package comparison article.

What’s Next

We are actively working on:

The library is open source and MIT licensed. We welcome pull requests, bug reports, and feature requests.

Try It Today

npm install @koshmoney/countries

Read the full API documentation or browse the source code on GitHub .

If you have questions or feedback, open an issue  on GitHub. We would love to hear what you are building.