@countrystatecity/countries-browser

Browser-native countries, states, and cities data with jsDelivr CDN and lazy loading
102,782 5
Install
npm install @countrystatecity/countries-browser
Latest Version:2.0.0
License:ODbL-1.0
Last Updated:Oct 18, 2026
Links: GitHub  ·  npm
Maintainer: dr5hn

@countrystatecity/countries-browser

npm CI npm downloads npm downloads

Browser-native countries, states, and cities data with jsDelivr CDN and lazy loading. Same API as the server package — works in React, Vue, Svelte, Vite, and any browser environment.

Environment: 🌐 Browser-native (React, Vue, Svelte, Vite, and any browser environment)

Upgrading to 2.0.0

Data now comes from upstream v4.0-export.50. The v3.2 → v4.0 transition removed 4,486 city ids (duplicates, US counties moved to a separate counties dataset, and non-places), reassigned 7,527 cities to different states, and added 27 states while retiring 6. Stored ids and per-state results can change; US counties are no longer returned as cities. Later export corrections are included in this snapshot.

Before upgrading stored city/state ids, use removed_ids_v3.2_to_v4.0.csv attached to the upstream v4.0 release. It identifies merged ids and their survivors, counties moved to new ids, and retired states and their successors. Match the record, outcome, and new_table columns before using new_id; a county id is not a city id. Refresh stored state associations against this snapshot.

City type uses standard terms (municipality, city, town, village, locality, section) for migrated countries; other countries can retain earlier terms such as adm1/adm2. This package intentionally projects a subset of upstream fields: type_local, county_id, level, and parent_id are omitted from city/state records, and no counties dataset is shipped. City translations remain empty objects; country translations are included. Use upstream exports when you need official local terms, county links, or hierarchy traversal.

✨ Features

  • 🌍 Complete Data: 250+ countries, 5,000+ states, 150,000+ cities
  • 🌐 Browser-Native: Runs directly in any browser via fetch and jsDelivr CDN
  • 🚀 Minimal Bundle: <10KB initial load, lazy-load everything else
  • 🔄 Lazy Loading: On-demand fetching — load only what you need
  • 🌐 Translations: 18+ languages supported
  • ⏰ Timezone Data: Full timezone information per location
  • 📝 TypeScript: Full type definitions included
  • 🔧 Configurable: Optional CDN override and cache tuning

📦 Installation

npm install @countrystatecity/countries-browser
# or
yarn add @countrystatecity/countries-browser
# or
pnpm add @countrystatecity/countries-browser

🚀 Quick Start

import { getCountries, getStatesOfCountry, getCitiesOfState } from '@countrystatecity/countries-browser';

// Load all countries (~30KB gzipped)
const countries = await getCountries();
console.log(countries[0]);
// { id: 1, name: "United States", iso2: "US", emoji: "🇺🇸", ... }

// Load states for a country (on-demand)
const states = await getStatesOfCountry('US');
console.log(states[0]);
// { id: 1, name: "California", iso2: "CA", ... }

// Load cities for a state (on-demand)
const cities = await getCitiesOfState('US', 'CA');
console.log(cities[0]);
// { id: 1, name: "Los Angeles", latitude: "34.05", ... }

Local package or live API?

This package ships a free, CDN-hosted snapshot of the data — that's still the right choice when the app must work offline-first, a periodic snapshot is enough, or you just need simple lookups/dropdowns.

The CountryStateCity API is worth adding alongside it when you need:

  • Freshness — data that updates without publishing a new package version.
  • Search — fuzzy, typo-tolerant, server-side search (csc.search.fuzzy(...)), not just exact-match local lookups.
  • Scale & support — configurable rate limits and support beyond a bundled snapshot.

Start free with 3,000 requests per month. Compare plans or get an API key.

npm install @countrystatecity/sdk
// Before
import { getCountries, getStatesOfCountry, getCitiesOfState } from '@countrystatecity/countries-browser';

const countries = await getCountries();
const states = await getStatesOfCountry('US');
const cities = await getCitiesOfState('US', 'CA');
// After
import { createCSCClient } from '@countrystatecity/sdk';

const csc = createCSCClient({ apiKey: process.env.CSC_API_KEY! });

const { data: countries } = await csc.countries.list();
const { data: states } = await csc.states.list({ country: 'US' });
const { data: cities } = await csc.cities.list({ country: 'US', state: 'CA' });

See the full migration guide for error handling, retries, and more.

📖 API Reference

Core Functions

getCountries()

Get lightweight list of all countries (basic info only).

  • Returns: Promise<ICountry[]>
getCountryByCode(code: string)

Get full country metadata including timezones and translations.

  • Parameters: code - ISO2 code (e.g., 'US')
  • Returns: Promise<ICountryMeta | null>
getStatesOfCountry(countryCode: string)

Get all states/provinces for a country.

  • Parameters: countryCode - ISO2 code
  • Returns: Promise<IState[]>
getStateByCode(countryCode: string, stateCode: string)

Get specific state details.

  • Parameters: countryCode, stateCode
  • Returns: Promise<IState | null>
getCitiesOfState(countryCode: string, stateCode: string)

Get all cities in a specific state.

  • Parameters: countryCode, stateCode
  • Returns: Promise<ICity[]>
getCityById(countryCode: string, stateCode: string, cityId: number)

Get a specific city by its numeric ID.

  • Parameters: countryCode, stateCode, cityId
  • Returns: Promise<ICity | null>
getAllCitiesOfCountry(countryCode: string)

Get ALL cities in an entire country.

  • Warning: Large data size, use sparingly
  • Returns: Promise<ICity[]>
getAllCitiesInWorld()

Get every city globally.

  • Warning: MASSIVE data (8MB+), rarely needed
  • Returns: Promise<ICity[]>
getRegions()

Get list of all regions (continents).

  • Returns: Promise<IRegion[]>
getSubregions()

Get list of all subregions.

  • Returns: Promise<ISubregion[]>

Utility Functions

isValidCountryCode(code: string)

Check if country code exists.

  • Returns: Promise<boolean>
isValidStateCode(countryCode: string, stateCode: string)

Check if state code exists in a country.

  • Returns: Promise<boolean>
searchCitiesByName(countryCode: string, stateCode: string, term: string)

Search cities by partial name match.

  • Returns: Promise<ICity[]>
getCountryNameByCode(code: string)

Get country name from code.

  • Returns: Promise<string | null>
getStateNameByCode(countryCode: string, stateCode: string)

Get state name from code.

  • Returns: Promise<string | null>
getTimezoneForCity(countryCode: string, stateCode: string, cityName: string)

Get timezone for specific city.

  • Returns: Promise<string | null>
getCountryTimezones(countryCode: string)

Get all timezones for a country.

  • Returns: Promise<string[]>
getSubregionsOfRegion(regionId: number)

Get all subregions belonging to a region.

  • Returns: Promise<ISubregion[]>
getCountriesByRegion(region: string | number)

Get all countries in a region, by name (case-insensitive, e.g. 'Europe') or region ID.

  • Returns: Promise<ICountry[]>
getCountriesBySubregion(subregion: string | number)

Get all countries in a subregion, by name (case-insensitive, e.g. 'South America') or subregion ID.

  • Returns: Promise<ICountry[]>

Configuration

import { configure, resetConfiguration, clearCache } from '@countrystatecity/countries-browser';

// Self-host data instead of jsDelivr
configure({
  baseURL: 'https://my-cdn.com/data',
  timeout: 10000,
  cacheSize: 100,
});

// Reset to defaults
resetConfiguration();

// Clear in-memory cache
clearCache();

React Example

import { useState, useEffect } from 'react';
import { getCountries, getStatesOfCountry, getCitiesOfState } from '@countrystatecity/countries-browser';
import type { ICountry, IState, ICity } from '@countrystatecity/countries-browser';

export function LocationSelector() {
  const [countries, setCountries] = useState<ICountry[]>([]);
  const [states, setStates] = useState<IState[]>([]);
  const [cities, setCities] = useState<ICity[]>([]);
  const [selectedCountry, setSelectedCountry] = useState('');
  const [selectedState, setSelectedState] = useState('');

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

  useEffect(() => {
    if (!selectedCountry) { setStates([]); return; }
    getStatesOfCountry(selectedCountry).then(setStates);
  }, [selectedCountry]);

  useEffect(() => {
    if (!selectedCountry || !selectedState) { setCities([]); return; }
    getCitiesOfState(selectedCountry, selectedState).then(setCities);
  }, [selectedCountry, selectedState]);

  return (
    <div>
      <select value={selectedCountry} onChange={(e) => { setSelectedCountry(e.target.value); setSelectedState(''); }}>
        <option value="">Select Country</option>
        {countries.map(c => <option key={c.iso2} value={c.iso2}>{c.name}</option>)}
      </select>
      <select value={selectedState} onChange={(e) => setSelectedState(e.target.value)} disabled={!selectedCountry}>
        <option value="">Select State</option>
        {states.map(s => <option key={s.iso2} value={s.iso2}>{s.name}</option>)}
      </select>
      <select disabled={!selectedState}>
        <option value="">Select City</option>
        {cities.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
      </select>
    </div>
  );
}

Error Handling

import { getCountries, NetworkError } from '@countrystatecity/countries-browser';

try {
  const countries = await getCountries();
} catch (error) {
  if (error instanceof NetworkError) {
    console.error(`CDN request failed: ${error.statusCode} at ${error.url}`);
  }
}

Invalid codes return null or [] gracefully — no exceptions thrown.

🔧 TypeScript Types

import type { ICountry, ICountryMeta, IState, ICity, IRegion, ISubregion, ITimezone } from '@countrystatecity/countries-browser';

📊 Bundle Size

Action Bundle Size
Install & import ~5KB
Load countries ~30KB gzipped
Load US states ~30KB
Load CA cities ~15KB
Typical usage ~50KB

Server vs Browser Comparison

@countrystatecity/countries (server) @countrystatecity/countries-browser
Environment Node.js only Browser + Node.js
Data loading fs.readFileSync fetch() from CDN
Initial bundle ~5KB ~5KB
Configuration None needed Optional CDN override
Lazy loading Yes Yes

🧪 Testing

# Run all tests
npm test

# Watch mode
npm run test:watch

All packages include comprehensive tests:

  • ✅ Unit tests
  • ✅ Integration tests
  • ✅ iOS/Safari compatibility tests

🔄 CI/CD & Automation

Continuous Integration

Every push and PR automatically:

  • ✅ Runs type checking
  • ✅ Executes comprehensive tests
  • ✅ Builds the package
  • ✅ Validates bundle sizes
  • ✅ Tests iOS/Safari compatibility

Automated Publishing

Automated publishing to NPM on version changes:

  • 🔍 Detects version bumps in package.json
  • 📦 Builds and tests before publishing
  • 🚀 Publishes to NPM registry
  • 🏷️ Creates GitHub release with changelog

📄 License

ODbL-1.0 © dr5hn

This package and its data are licensed under the Open Database License (ODbL) v1.0. The data is sourced from the Countries States Cities Database which is also licensed under ODbL-1.0.

You are free to share, create, and adapt this database as long as you attribute the original sources, distribute adaptations under the same license, and don't use technical restrictions to lock down the data.

🤝 Contributing

Contributions are welcome! Please open an issue or PR.

For data-related issues (incorrect country names, missing cities, wrong coordinates, etc.), please report them to the Countries States Cities Database repository, which is the source of data for this package.

📦 Package Ecosystem

This package is part of the @countrystatecity package ecosystem:

  • @countrystatecity/countries — Server-side countries, states, and cities database. Environment: Node.js, Next.js API routes, Express. Bundle: <10KB initial load.

  • @countrystatecity/countries-browser (This package) — Browser-native version with jsDelivr CDN and lazy loading. Environment: React, Vue, Svelte, Vite, any browser. Same API as the server package — zero config, just import and use.

  • @countrystatecity/timezones — Comprehensive timezone data with conversion utilities. Environment: Server-side only. Bundle: <20KB initial load.

🔗 Links

Related Packages

@frontman-ai/vite

Vite integration for Frontman - AI-powered development tools

21,987 719
@nodefony/frontend

Construction et rechargement à chaud des frontends de chaque module Nodefony — V...

2,139 0
@ashwindojo/codeatlas-vite-plugin

Vite plugin for CodeAtlas — real-time annotation parsing and live MCP HTTP serve...

1,356
sveltease

Seamlessly integrate svelte with vue, react with vite, react with cra and next.j...

1,274 11
@quick-start/create-electron

An easy way to start an Electron project

115,010 363

Version History

Version Released License
2.0.0 ODbL-1.0
1.0.4 ODbL-1.0
1.0.3 ODbL-1.0
1.0.2 ODbL-1.0
1.0.1 ODbL-1.0