@tw-labs/countries 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,333 @@
1
+ <div align="center">
2
+
3
+ # @tw-labs/countries
4
+
5
+ **Comprehensive country data with utilities and an optional React hook.**
6
+
7
+ TypeScript-first · Framework-agnostic · Tree-shakeable · Zero dependencies
8
+
9
+ <br />
10
+
11
+ [![npm version](https://img.shields.io/npm/v/@tw-labs/countries?style=flat-square&logo=npm&logoColor=white&color=CB3837)](https://www.npmjs.com/package/@tw-labs/countries)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](https://opensource.org/licenses/MIT)
13
+ [![TypeScript](https://img.shields.io/badge/TypeScript-ready-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org)
14
+ [![Tree Shakeable](https://img.shields.io/badge/Tree%20Shakeable-yes-brightgreen?style=flat-square)](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking)
15
+ [![Next.js](https://img.shields.io/badge/Next.js-compatible-black?style=flat-square&logo=next.js&logoColor=white)](https://nextjs.org)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## Features
22
+
23
+ - **195+ countries** — ISO alpha-2/alpha-3 codes, name, phone dial code, capital, continent, currency, and timezones
24
+ - **Flag support** — emoji flags, PNG (20px, 40px), and SVG via [flagcdn.com](https://flagcdn.com)
25
+ - **Tree-shakeable** — import only the countries you need via the `/data` subpath
26
+ - **Framework-agnostic core** — works in Node.js, Vue, Svelte, and anywhere else
27
+ - **React hook** — `useCountries` with filtering, sorting, and conditional flag types
28
+ - **Next.js ready** — `"use client"` built into the React entry, core works in Server Components
29
+ - **TypeScript-first** — full type safety and IntelliSense out of the box
30
+ - **Zero dependencies** — no runtime dependencies
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ <table>
37
+ <tr>
38
+ <td>
39
+ <img src="https://img.shields.io/badge/npm-CB3837?style=flat-square&logo=npm&logoColor=white" alt="npm" />
40
+ </td>
41
+ <td>
42
+ <code>npm install @tw-labs/countries</code>
43
+ </td>
44
+ </tr>
45
+ <tr>
46
+ <td>
47
+ <img src="https://img.shields.io/badge/yarn-2C8EBB?style=flat-square&logo=yarn&logoColor=white" alt="yarn" />
48
+ </td>
49
+ <td>
50
+ <code>yarn add @tw-labs/countries</code>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <td>
55
+ <img src="https://img.shields.io/badge/pnpm-F69220?style=flat-square&logo=pnpm&logoColor=white" alt="pnpm" />
56
+ </td>
57
+ <td>
58
+ <code>pnpm add @tw-labs/countries</code>
59
+ </td>
60
+ </tr>
61
+ <tr>
62
+ <td>
63
+ <img src="https://img.shields.io/badge/bun-000000?style=flat-square&logo=bun&logoColor=white" alt="bun" />
64
+ </td>
65
+ <td>
66
+ <code>bun add @tw-labs/countries</code>
67
+ </td>
68
+ </tr>
69
+ </table>
70
+
71
+ ---
72
+
73
+ ## Import Paths
74
+
75
+ | Path | Use case |
76
+ |---|---|
77
+ | `@tw-labs/countries` | Framework-agnostic utilities (Node, Vue, Svelte, Next.js server) |
78
+ | `@tw-labs/countries/react` | React hook (`useCountries`) |
79
+ | `@tw-labs/countries/data` | Named country exports — fully tree-shakeable |
80
+
81
+ ---
82
+
83
+ ## Core Utilities
84
+
85
+ ```typescript
86
+ import {
87
+ getAllCountries,
88
+ getAllCountriesWithFlags,
89
+ getCountryByCode,
90
+ getCountryByAlpha3,
91
+ getCountryByPhone,
92
+ withFlags,
93
+ } from "@tw-labs/countries";
94
+ ```
95
+
96
+ ### `getCountryByCode(code)`
97
+
98
+ ```typescript
99
+ const country = getCountryByCode("US");
100
+ // {
101
+ // code: "US",
102
+ // alpha3: "USA",
103
+ // label: "United States",
104
+ // phone: "1",
105
+ // capital: "Washington, D.C.",
106
+ // continent: "North America",
107
+ // currency: { code: "USD", symbol: "$", name: "United States Dollar" },
108
+ // timezones: ["America/New_York", "America/Chicago", ...]
109
+ // }
110
+ ```
111
+
112
+ ### `getCountryByAlpha3(alpha3)`
113
+
114
+ ```typescript
115
+ const country = getCountryByAlpha3("GBR");
116
+ // { code: "GB", alpha3: "GBR", label: "United Kingdom", ... }
117
+ ```
118
+
119
+ ### `getCountryByPhone(phone)`
120
+
121
+ Returns an array since multiple countries can share a dial code.
122
+
123
+ ```typescript
124
+ const countries = getCountryByPhone("1");
125
+ // [{ code: "US", ... }, { code: "CA", ... }, { code: "PR", ... }, ...]
126
+ ```
127
+
128
+ ### `getAllCountries(options?)`
129
+
130
+ ```typescript
131
+ // All countries
132
+ const all = getAllCountries();
133
+
134
+ // Filtered
135
+ const european = getAllCountries({
136
+ filter: (c) => c.continent === "Europe",
137
+ });
138
+
139
+ // Sorted
140
+ const sorted = getAllCountries({
141
+ sort: { by: "label", order: "asc" },
142
+ });
143
+
144
+ // Filtered + sorted
145
+ const result = getAllCountries({
146
+ filter: (c) => c.continent === "Asia",
147
+ sort: { by: "label", order: "asc" },
148
+ });
149
+ ```
150
+
151
+ ### `getAllCountriesWithFlags(options?)`
152
+
153
+ Same as `getAllCountries` but each country includes flag data.
154
+
155
+ ```typescript
156
+ const countries = getAllCountriesWithFlags();
157
+ // Each country has: flagEmoji, flagPng20, flagPng40, flagSvg, srcSet
158
+ ```
159
+
160
+ ### `withFlags(country)`
161
+
162
+ Add flag data to a single country object.
163
+
164
+ ```typescript
165
+ const country = getCountryByCode("JP");
166
+ const withFlagData = withFlags(country!);
167
+ // { ...country, flagEmoji: "🇯🇵", flagPng20: "...", flagSvg: "...", ... }
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Tree-Shakeable Data Imports
173
+
174
+ For the best bundle size when you know your countries at build time:
175
+
176
+ ```typescript
177
+ import { US, GB, DE, FR } from "@tw-labs/countries/data";
178
+ // Only these 4 country objects are included in your bundle
179
+ ```
180
+
181
+ > **Note:** `getCountryByCode("US")` is for runtime dynamic lookups and includes the full dataset. Use the `/data` subpath when the country codes are known at build time.
182
+
183
+ ---
184
+
185
+ ## React Hook
186
+
187
+ ```typescript
188
+ import { useCountries } from "@tw-labs/countries/react";
189
+ ```
190
+
191
+ The `@tw-labs/countries/react` entry has `"use client"` built in — no need to add it yourself in Next.js.
192
+
193
+ ### Basic usage
194
+
195
+ ```typescript
196
+ function CountryList() {
197
+ const countries = useCountries({});
198
+
199
+ return (
200
+ <ul>
201
+ {countries.map((c) => (
202
+ <li key={c.code}>
203
+ {c.label} (+{c.phone})
204
+ </li>
205
+ ))}
206
+ </ul>
207
+ );
208
+ }
209
+ ```
210
+
211
+ ### With flags
212
+
213
+ ```typescript
214
+ function FlagList() {
215
+ const countries = useCountries({ includeFlags: true });
216
+ // TypeScript knows each country has flagEmoji, flagPng20, etc.
217
+
218
+ return (
219
+ <div>
220
+ {countries.map((c) => (
221
+ <div key={c.code}>
222
+ <span>{c.flagEmoji}</span>
223
+ <img src={c.flagPng40} alt={`${c.label} flag`} />
224
+ <span>{c.label}</span>
225
+ </div>
226
+ ))}
227
+ </div>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ### With filtering and sorting
233
+
234
+ ```typescript
235
+ function SearchableCountryPicker() {
236
+ const [query, setQuery] = useState("");
237
+
238
+ const countries = useCountries({
239
+ includeFlags: true,
240
+ filter: query
241
+ ? (c) =>
242
+ c.label.toLowerCase().includes(query.toLowerCase()) ||
243
+ c.code.toLowerCase().includes(query.toLowerCase()) ||
244
+ c.phone.includes(query)
245
+ : undefined,
246
+ sort: { by: "label", order: "asc" },
247
+ });
248
+
249
+ return (
250
+ <div>
251
+ <input
252
+ placeholder="Search countries..."
253
+ value={query}
254
+ onChange={(e) => setQuery(e.target.value)}
255
+ />
256
+ <p>{countries.length} results</p>
257
+ {countries.map((c) => (
258
+ <div key={c.code}>
259
+ {c.flagEmoji} {c.label} (+{c.phone})
260
+ </div>
261
+ ))}
262
+ </div>
263
+ );
264
+ }
265
+ ```
266
+
267
+ ### Sort options
268
+
269
+ ```typescript
270
+ useCountries({ sort: { by: "label", order: "asc" } }); // A → Z by name
271
+ useCountries({ sort: { by: "code", order: "asc" } }); // AD → ZW
272
+ useCountries({ sort: { by: "phone", order: "asc" } }); // by dial code
273
+ useCountries({ sort: { by: "continent", order: "asc" } }); // grouped by continent
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Next.js Usage
279
+
280
+ ```typescript
281
+ // app/components/PhoneInput.tsx — Client Component
282
+ // No need to add "use client" manually — it's built into @tw-labs/countries/react
283
+ import { useCountries } from "@tw-labs/countries/react";
284
+
285
+ // app/api/validate/route.ts — Server Component / Route Handler
286
+ // Works with no configuration — no "use client" in the core entry
287
+ import { getCountryByCode } from "@tw-labs/countries";
288
+ ```
289
+
290
+ ---
291
+
292
+ ## TypeScript Types
293
+
294
+ ```typescript
295
+ import type {
296
+ Country,
297
+ CountryWithFlags,
298
+ Currency,
299
+ FilterSortOptions,
300
+ SortBy,
301
+ } from "@tw-labs/countries";
302
+
303
+ type Currency = {
304
+ code: string; // "USD"
305
+ symbol: string; // "$"
306
+ name: string; // "United States Dollar"
307
+ };
308
+
309
+ type Country = {
310
+ code: string; // ISO alpha-2 "US"
311
+ alpha3: string; // ISO alpha-3 "USA"
312
+ label: string; // "United States"
313
+ phone: string; // "1"
314
+ capital: string; // "Washington, D.C."
315
+ continent: string; // "North America"
316
+ currency: Currency;
317
+ timezones: string[]; // ["America/New_York", ...]
318
+ };
319
+
320
+ type CountryWithFlags = Country & {
321
+ flagEmoji: string; // "🇺🇸"
322
+ flagPng20: string; // https://flagcdn.com/w20/us.png
323
+ flagPng40: string; // https://flagcdn.com/w40/us.png
324
+ flagSvg: string; // https://flagcdn.com/us.svg
325
+ srcSet: string; // https://flagcdn.com/w40/us.png 2x
326
+ };
327
+ ```
328
+
329
+ ---
330
+
331
+ ## License
332
+
333
+ MIT © [TechWebster](https://techwebster.com)