@tw-labs/countries
v0.0.2
Published
Comprehensive country data with utilities and React hook — tree-shakeable, TypeScript-first, framework-agnostic
Maintainers
Readme
@tw-labs/countries
Comprehensive country data with utilities and an optional React hook.
TypeScript-first · Framework-agnostic · Tree-shakeable · Zero dependencies
Features
- 195+ countries — ISO alpha-2/alpha-3 codes, name, phone dial code, capital, continent, currency, and timezones
- Flag support — emoji flags, PNG (20px, 40px), and SVG via flagcdn.com
- Tree-shakeable — import only the countries you need via the
/datasubpath - Framework-agnostic core — works in Node.js, Vue, Svelte, and anywhere else
- React hook —
useCountrieswith filtering, sorting, and conditional flag types - Next.js ready —
"use client"built into the React entry, core works in Server Components - TypeScript-first — full type safety and IntelliSense out of the box
- Zero dependencies — no runtime dependencies
Installation
npm install @tw-labs/countriesyarn add @tw-labs/countriespnpm add @tw-labs/countriesbun add @tw-labs/countriesImport Paths
| Path | Use case |
|---|---|
| @tw-labs/countries | Framework-agnostic utilities (Node, Vue, Svelte, Next.js server) |
| @tw-labs/countries/react | React hook (useCountries) |
| @tw-labs/countries/data | Named country exports — fully tree-shakeable |
Core Utilities
import {
getAllCountries,
getAllCountriesWithFlags,
getCountryByCode,
getCountryByAlpha3,
getCountryByPhone,
withFlags,
} from "@tw-labs/countries";getCountryByCode(code)
const country = getCountryByCode("US");
// {
// code: "US",
// alpha3: "USA",
// label: "United States",
// phone: "1",
// capital: "Washington, D.C.",
// continent: "North America",
// currency: { code: "USD", symbol: "$", name: "United States Dollar" },
// timezones: ["America/New_York", "America/Chicago", ...]
// }getCountryByAlpha3(alpha3)
const country = getCountryByAlpha3("GBR");
// { code: "GB", alpha3: "GBR", label: "United Kingdom", ... }getCountryByPhone(phone)
Returns an array since multiple countries can share a dial code.
const countries = getCountryByPhone("1");
// [{ code: "US", ... }, { code: "CA", ... }, { code: "PR", ... }, ...]getAllCountries(options?)
// All countries
const all = getAllCountries();
// Filtered
const european = getAllCountries({
filter: (c) => c.continent === "Europe",
});
// Sorted
const sorted = getAllCountries({
sort: { by: "label", order: "asc" },
});
// Filtered + sorted
const result = getAllCountries({
filter: (c) => c.continent === "Asia",
sort: { by: "label", order: "asc" },
});getAllCountriesWithFlags(options?)
Same as getAllCountries but each country includes flag data.
const countries = getAllCountriesWithFlags();
// Each country has: flagEmoji, flagPng20, flagPng40, flagSvg, srcSetwithFlags(country)
Add flag data to a single country object.
const country = getCountryByCode("JP");
const withFlagData = withFlags(country!);
// { ...country, flagEmoji: "🇯🇵", flagPng20: "...", flagSvg: "...", ... }Tree-Shakeable Data Imports
For the best bundle size when you know your countries at build time:
import { US, GB, DE, FR } from "@tw-labs/countries/data";
// Only these 4 country objects are included in your bundleNote:
getCountryByCode("US")is for runtime dynamic lookups and includes the full dataset. Use the/datasubpath when the country codes are known at build time.
React Hook
import { useCountries } from "@tw-labs/countries/react";The @tw-labs/countries/react entry has "use client" built in — no need to add it yourself in Next.js.
Basic usage
function CountryList() {
const countries = useCountries({});
return (
<ul>
{countries.map((c) => (
<li key={c.code}>
{c.label} (+{c.phone})
</li>
))}
</ul>
);
}With flags
function FlagList() {
const countries = useCountries({ includeFlags: true });
// TypeScript knows each country has flagEmoji, flagPng20, etc.
return (
<div>
{countries.map((c) => (
<div key={c.code}>
<span>{c.flagEmoji}</span>
<img src={c.flagPng40} alt={`${c.label} flag`} />
<span>{c.label}</span>
</div>
))}
</div>
);
}With filtering and sorting
function SearchableCountryPicker() {
const [query, setQuery] = useState("");
const countries = useCountries({
includeFlags: true,
filter: query
? (c) =>
c.label.toLowerCase().includes(query.toLowerCase()) ||
c.code.toLowerCase().includes(query.toLowerCase()) ||
c.phone.includes(query)
: undefined,
sort: { by: "label", order: "asc" },
});
return (
<div>
<input
placeholder="Search countries..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<p>{countries.length} results</p>
{countries.map((c) => (
<div key={c.code}>
{c.flagEmoji} {c.label} (+{c.phone})
</div>
))}
</div>
);
}Sort options
useCountries({ sort: { by: "label", order: "asc" } }); // A → Z by name
useCountries({ sort: { by: "code", order: "asc" } }); // AD → ZW
useCountries({ sort: { by: "phone", order: "asc" } }); // by dial code
useCountries({ sort: { by: "continent", order: "asc" } }); // grouped by continentNext.js Usage
// app/components/PhoneInput.tsx — Client Component
// No need to add "use client" manually — it's built into @tw-labs/countries/react
import { useCountries } from "@tw-labs/countries/react";
// app/api/validate/route.ts — Server Component / Route Handler
// Works with no configuration — no "use client" in the core entry
import { getCountryByCode } from "@tw-labs/countries";TypeScript Types
import type {
Country,
CountryWithFlags,
Currency,
FilterSortOptions,
SortBy,
} from "@tw-labs/countries";
type Currency = {
code: string; // "USD"
symbol: string; // "$"
name: string; // "United States Dollar"
};
type Country = {
code: string; // ISO alpha-2 "US"
alpha3: string; // ISO alpha-3 "USA"
label: string; // "United States"
phone: string; // "1"
capital: string; // "Washington, D.C."
continent: string; // "North America"
currency: Currency;
timezones: string[]; // ["America/New_York", ...]
};
type CountryWithFlags = Country & {
flagEmoji: string; // "🇺🇸"
flagPng20: string; // https://flagcdn.com/w20/us.png
flagPng40: string; // https://flagcdn.com/w40/us.png
flagSvg: string; // https://flagcdn.com/us.svg
srcSet: string; // https://flagcdn.com/w40/us.png 2x
};License
MIT © TechWebster
