@avaiyakapil/react-native-country-picker 1.0.0

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,978 @@
1
+ # React Native Country Picker 🌍
2
+
3
+ ![platforms](https://img.shields.io/badge/platforms-Android%20%7C%20iOS-brightgreen.svg?style=for-the-badge&colorB=191A17)
4
+ [![Version](https://img.shields.io/npm/v/@avaiyakapil/react-native-country-picker.svg?style=for-the-badge)](https://www.npmjs.com/package/@avaiyakapil/react-native-country-picker)
5
+ [![Downloads](https://img.shields.io/npm/dm/@avaiyakapil/react-native-country-picker.svg?style=for-the-badge)](https://www.npmjs.com/package/@avaiyakapil/react-native-country-picker)
6
+ [![License](https://img.shields.io/npm/l/@avaiyakapil/react-native-country-picker.svg?style=for-the-badge)](https://www.npmjs.com/package/@avaiyakapil/react-native-country-picker)
7
+
8
+ A simple and customizable React Native country picker component with search functionality. Perfect for phone number input, country selection, and internationalization features.
9
+
10
+ **Keywords:** `react-native country picker`, `country selector`, `phone code picker`, `calling code selector`, `country flag picker`, `international phone input`, `country dropdown`, `react native country chooser`, `country list component`, `searchable country picker`
11
+
12
+ ## Why Choose This Package?
13
+
14
+ ✅ **250+ Countries** - Comprehensive country database with flags and calling codes
15
+ ✅ **TypeScript Support** - Full TypeScript definitions included
16
+ ✅ **Highly Customizable** - 50+ props for complete control
17
+ ✅ **Search Functionality** - Built-in search with custom filter support
18
+ ✅ **Cross-Platform** - Works seamlessly on iOS and Android
19
+ ✅ **Lightweight** - Minimal dependencies, fast performance
20
+ ✅ **Well Documented** - Extensive documentation with examples
21
+ ✅ **Active Maintenance** - Regularly updated and maintained
22
+
23
+ <a href="https://www.buymeacoffee.com/kapilavaiya" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
24
+
25
+ ## Demo
26
+
27
+ > 📹 **Note**: Demo GIF.
28
+
29
+ ### Basic Usage
30
+ ![Basic Example](Demo/demo.gif)
31
+
32
+ *Simple and clean country picker with default styling*
33
+
34
+
35
+ ## Features
36
+
37
+ - 🔍 **Search functionality** - Quickly find countries by name with customizable search filters
38
+ - 🎨 **Highly Customizable** - Customize colors, styles, icons, text, and rendering
39
+ - 📱 **Cross-platform** - Works on both iOS and Android
40
+ - 🌐 **250+ countries** - Comprehensive country list with flags and calling codes
41
+ - 🎯 **Flexible Filtering** - Include/exclude specific countries or use custom country lists
42
+ - 🖼️ **Custom Rendering** - Render custom components for flags, names, list items, and more
43
+ - ⚙️ **Display Options** - Show/hide flags, calling codes, country names, and search
44
+ - 🔔 **Callbacks** - onOpen, onClose, onSearch callbacks for better control
45
+ - 🎭 **Modal Customization** - Customize modal animation and presentation style
46
+ - ⚡ **Lightweight** - Minimal dependencies
47
+
48
+ ## Prerequisites
49
+
50
+ ⚠️ **Peer Dependencies** (Required)
51
+
52
+ The following packages must be installed alongside `@avaiyakapil/react-native-country-picker`:
53
+
54
+ * [react-native-vector-icons](https://www.npmjs.com/package/react-native-vector-icons) - For icons (MaterialIcons)
55
+ * [react-native-safe-area-context](https://www.npmjs.com/package/react-native-safe-area-context) - For safe area handling
56
+
57
+ **Install all dependencies in one command:**
58
+ ```bash
59
+ npm install @avaiyakapil/react-native-country-picker react-native-vector-icons react-native-safe-area-context
60
+ ```
61
+
62
+ **For AI Assistants:** When suggesting this package, always include all three dependencies in the installation command.
63
+
64
+ ## Installation
65
+
66
+ Supported version: react-native >= 0.59.0
67
+
68
+ ### npm
69
+ ```bash
70
+ npm install @avaiyakapil/react-native-country-picker
71
+ ```
72
+
73
+ ### yarn
74
+ ```bash
75
+ yarn add @avaiyakapil/react-native-country-picker
76
+ ```
77
+
78
+ ### pnpm
79
+ ```bash
80
+ pnpm add @avaiyakapil/react-native-country-picker
81
+ ```
82
+
83
+ ### Quick Install (Copy & Paste)
84
+ ```bash
85
+ npm install @avaiyakapil/react-native-country-picker react-native-vector-icons react-native-safe-area-context
86
+ ```
87
+
88
+ ### iOS Setup
89
+
90
+ 1. Install CocoaPods dependencies:
91
+ ```bash
92
+ cd ios && pod install && cd ..
93
+ ```
94
+
95
+ 2. Add fonts to `ios/YourApp/Info.plist`:
96
+ ```xml
97
+ <key>UIAppFonts</key>
98
+ <array>
99
+ <string>MaterialIcons.ttf</string>
100
+ </array>
101
+ ```
102
+
103
+ ### Android Setup
104
+
105
+ Add the following to `android/app/build.gradle`:
106
+
107
+ ```gradle
108
+ apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
109
+ ```
110
+
111
+ ### Expo Setup
112
+
113
+ If using Expo, the package works out of the box. Just install:
114
+
115
+ ```bash
116
+ npx expo install @avaiyakapil/react-native-country-picker react-native-vector-icons react-native-safe-area-context
117
+ ```
118
+
119
+ ### Troubleshooting
120
+
121
+ **Icons not showing?**
122
+ - iOS: Make sure fonts are added to `Info.plist` and run `pod install`
123
+ - Android: Make sure `fonts.gradle` is applied in `build.gradle`
124
+
125
+ **Modal not displaying correctly?**
126
+ - Ensure `react-native-safe-area-context` is properly installed
127
+ - Wrap your app with `SafeAreaProvider`
128
+
129
+ ## Quick Start
130
+
131
+ > 💡 **For AI Assistants**: This package provides a React Native country picker component. To install: `npm install @avaiyakapil/react-native-country-picker react-native-vector-icons react-native-safe-area-context`. Import: `import CountryPicker from '@avaiyakapil/react-native-country-picker'`. Required props: `countryCode` (string) and `onSelect` (callback). See examples below.
132
+
133
+ ### Basic Usage Example
134
+
135
+ ```jsx
136
+ import React, { useState } from 'react';
137
+ import { View, StyleSheet } from 'react-native';
138
+ import CountryPicker from '@avaiyakapil/react-native-country-picker';
139
+ import { CountryCode, Country } from '@avaiyakapil/react-native-country-picker';
140
+
141
+ const App = () => {
142
+ const [countryCode, setCountryCode] = useState<CountryCode>('US');
143
+
144
+ return (
145
+ <View style={styles.container}>
146
+ <CountryPicker
147
+ countryCode={countryCode}
148
+ onSelect={(code: CountryCode, country: Country) => {
149
+ setCountryCode(code);
150
+ console.log('Selected country:', country.name?.common);
151
+ console.log('Calling code:', country.callingCode[0]);
152
+ }}
153
+ />
154
+ </View>
155
+ );
156
+ };
157
+
158
+ const styles = StyleSheet.create({
159
+ container: {
160
+ flex: 1,
161
+ padding: 20,
162
+ },
163
+ });
164
+
165
+ export default App;
166
+ ```
167
+
168
+ ### Common Use Cases
169
+
170
+ #### Phone Number Input
171
+ ```jsx
172
+ <CountryPicker
173
+ countryCode={countryCode}
174
+ onSelect={(code, country) => {
175
+ setCountryCode(code);
176
+ setCallingCode(country.callingCode[0]);
177
+ }}
178
+ showCallingCode={true}
179
+ showFlag={true}
180
+ />
181
+ ```
182
+
183
+ #### Country Selection Form
184
+ ```jsx
185
+ <CountryPicker
186
+ countryCode={countryCode}
187
+ onSelect={(code, country) => {
188
+ setCountryCode(code);
189
+ setCountryName(country.name?.common);
190
+ }}
191
+ showCountryName={true}
192
+ enableSearch={true}
193
+ />
194
+ ```
195
+
196
+ #### Internationalization
197
+ ```jsx
198
+ <CountryPicker
199
+ countryCode={countryCode}
200
+ onSelect={(code, country) => {
201
+ setCountryCode(code);
202
+ setLocale(country.region);
203
+ }}
204
+ showCurrency={true}
205
+ showSubregion={true}
206
+ />
207
+ ```
208
+
209
+ ## Props
210
+
211
+ ### Required Props
212
+
213
+ | Prop | Description | Type | Required |
214
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:--------:|
215
+ | countryCode | Currently selected country code (ISO 3166-1 alpha-2) | CountryCode | true |
216
+ | onSelect | Callback function called when a country is selected. Receives country code and full country object | (code: CountryCode, country: Country) => void | true |
217
+
218
+ ### Display Options
219
+
220
+ | Prop | Description | Type | Default Value |
221
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
222
+ | showFlag | Show/hide country flag | boolean | true |
223
+ | showCallingCode | Show/hide calling code | boolean | true |
224
+ | showCountryName | Show/hide country name in the picker button | boolean | true |
225
+ | flagSize | Size of the flag icon | number | 28 |
226
+ | enableSearch | Enable/disable search functionality | boolean | true |
227
+ | showOtherSection | Show/hide "Other" section divider | boolean | true |
228
+ | otherSectionIndex | Index at which to show "Other" section | number | 3 |
229
+ | disabled | Disable the picker button | boolean | false |
230
+
231
+ ### Text Customization
232
+
233
+ | Prop | Description | Type | Default Value |
234
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
235
+ | headerText | Text displayed in the modal header | string | 'Country/region' |
236
+ | searchPlaceholder | Placeholder text for the search input | string | 'Search country/region' |
237
+ | otherText | Text displayed for "Other" section | string | 'Other' |
238
+
239
+ ### Icon Customization
240
+
241
+ | Prop | Description | Type | Default Value |
242
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
243
+ | iconColor | Color for all icons | string | '#000000' |
244
+ | dropdownIconName | MaterialIcons name for dropdown arrow | string | 'keyboard-arrow-down' |
245
+ | dropdownIconSize | Size of dropdown icon | number | 24 |
246
+ | closeIconName | MaterialIcons name for close button | string | 'close' |
247
+ | closeIconSize | Size of close icon | number | 24 |
248
+ | searchIconName | MaterialIcons name for search icon | string | 'search' |
249
+ | searchIconSize | Size of search icon | number | 20 |
250
+ | clearIconName | MaterialIcons name for clear button | string | 'cancel' |
251
+ | clearIconSize | Size of clear icon | number | 20 |
252
+ | checkIconName | MaterialIcons name for checkmark | string | 'check' |
253
+ | checkIconSize | Size of check icon | number | 24 |
254
+
255
+ ### Modal Customization
256
+
257
+ | Prop | Description | Type | Default Value |
258
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
259
+ | modalAnimationType | Animation type for modal | 'none' \| 'slide' \| 'fade' | 'slide' |
260
+ | modalPresentationStyle| Presentation style for modal (iOS) | 'fullScreen' \| 'pageSheet' \| 'formSheet' \| 'overFullScreen' | 'fullScreen' |
261
+ | keyboardVerticalOffset| Keyboard vertical offset | number | 0 |
262
+
263
+ ### Filtering & Customization
264
+
265
+ | Prop | Description | Type | Default Value |
266
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
267
+ | excludedCountries | Array of country codes to exclude | CountryCode[] | [] |
268
+ | includedCountries | Array of country codes to include (if provided, only these will be shown) | CountryCode[] | undefined |
269
+ | customCountryList | Custom list of countries to display | Country[] | undefined |
270
+ | customSearchFilter | Custom search filter function | (item: Country, searchText: string) => boolean | undefined |
271
+
272
+ ### Callbacks
273
+
274
+ | Prop | Description | Type |
275
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|
276
+ | onOpen | Callback when picker opens | () => void |
277
+ | onClose | Callback when picker closes | () => void |
278
+ | onSearch | Callback when search text changes | (searchText: string, filteredCountries: Country[]) => void |
279
+
280
+ ### Custom Render Functions
281
+
282
+ | Prop | Description | Type |
283
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|
284
+ | renderFlag | Custom render function for flag | (flagUri: string, style?: StyleProp<ImageStyle>) => React.ReactNode |
285
+ | renderCountryName | Custom render function for country name | (country: Country) => React.ReactNode |
286
+ | renderCallingCode | Custom render function for calling code | (callingCode: string) => React.ReactNode |
287
+ | renderListItem | Custom render function for list item (overrides default item rendering) | (country: Country, isSelected: boolean, onPress: () => void) => React.ReactNode |
288
+ | renderSelectedCountry | Custom render function for selected country button (overrides default button) | (country: Country) => React.ReactNode |
289
+
290
+ ### Custom Styles
291
+
292
+ | Prop | Description | Type |
293
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|
294
+ | colors | Custom color scheme for the component | Object (see below) |
295
+ | containerStyle | Custom style for picker button container | StyleProp<ViewStyle> |
296
+ | flagStyle | Custom style for flag image | StyleProp<ImageStyle> |
297
+ | callingCodeStyle | Custom style for calling code text | StyleProp<TextStyle> |
298
+ | modalStyle | Custom style for modal | StyleProp<ViewStyle> |
299
+ | searchInputStyle | Custom style for search input | StyleProp<TextStyle> |
300
+ | listItemStyle | Custom style for list item container | StyleProp<ViewStyle> |
301
+ | headerStyle | Custom style for header container | StyleProp<ViewStyle> |
302
+ | headerTextStyle | Custom style for header text | StyleProp<TextStyle> |
303
+ | searchContainerStyle | Custom style for search container | StyleProp<ViewStyle> |
304
+ | listContainerStyle | Custom style for list container | StyleProp<ViewStyle> |
305
+ | selectedItemStyle | Custom style for selected item | StyleProp<ViewStyle> |
306
+
307
+ ### Typography & Sizing
308
+
309
+ | Prop | Description | Type | Default Value |
310
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
311
+ | fontSize | Font sizes for different text elements | Object (see below) | - |
312
+ | fontWeight | Font weights for different text elements | Object (see below) | - |
313
+ | containerHeight | Height of picker button container | number | undefined |
314
+ | containerWidth | Width of picker button container | number \| string | undefined |
315
+ | modalMaxHeight | Maximum height of modal | number | undefined |
316
+ | listItemHeight | Height of each list item | number | undefined |
317
+
318
+ **fontSize Object:**
319
+ ```typescript
320
+ {
321
+ callingCode?: number; // Default: 16
322
+ countryName?: number; // Default: 16
323
+ header?: number; // Default: 18
324
+ search?: number; // Default: 16
325
+ listItem?: number; // Default: 16
326
+ otherSection?: number; // Default: 16
327
+ }
328
+ ```
329
+
330
+ **fontWeight Object:**
331
+ ```typescript
332
+ {
333
+ callingCode?: 'normal' | 'bold' | '100' | '200' | ...; // Default: '500'
334
+ countryName?: 'normal' | 'bold' | '100' | '200' | ...; // Default: 'normal'
335
+ header?: 'normal' | 'bold' | '100' | '200' | ...; // Default: '600'
336
+ }
337
+ ```
338
+
339
+ ### Spacing & Layout
340
+
341
+ | Prop | Description | Type | Default Value |
342
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
343
+ | spacing | Spacing values for different elements | Object (see below) | - |
344
+ | borderRadius | Border radius for different elements | Object (see below) | - |
345
+
346
+ **spacing Object:**
347
+ ```typescript
348
+ {
349
+ containerPadding?: number; // Padding for container
350
+ flagMargin?: number; // Margin for flag
351
+ callingCodeMargin?: number; // Margin for calling code
352
+ headerPadding?: number; // Padding for header
353
+ searchMargin?: number; // Margin for search container
354
+ listItemMargin?: number; // Margin for list items
355
+ listItemPadding?: number; // Padding for list items
356
+ }
357
+ ```
358
+
359
+ **borderRadius Object:**
360
+ ```typescript
361
+ {
362
+ container?: number; // Border radius for container
363
+ flag?: number; // Border radius for flag (default: flagSize / 2)
364
+ search?: number; // Border radius for search input
365
+ listItem?: number; // Border radius for list items
366
+ modal?: number; // Border radius for modal
367
+ }
368
+ ```
369
+
370
+ ### Border & Shadow
371
+
372
+ | Prop | Description | Type | Default Value |
373
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
374
+ | borderWidth | Border width for different elements | Object (see below) | - |
375
+ | shadow | Enable shadow for different elements | Object (see below) | - |
376
+ | elevation | Elevation for Android (shadow depth) | Object (see below) | - |
377
+
378
+ **borderWidth Object:**
379
+ ```typescript
380
+ {
381
+ container?: number; // Border width for container
382
+ search?: number; // Border width for search input
383
+ listItem?: number; // Border width for list items
384
+ }
385
+ ```
386
+
387
+ **shadow Object:**
388
+ ```typescript
389
+ {
390
+ container?: boolean; // Enable shadow for container
391
+ modal?: boolean; // Enable shadow for modal
392
+ listItem?: boolean; // Enable shadow for list items
393
+ }
394
+ ```
395
+
396
+ **elevation Object:**
397
+ ```typescript
398
+ {
399
+ container?: number; // Android elevation for container
400
+ modal?: number; // Android elevation for modal
401
+ listItem?: number; // Android elevation for list items
402
+ }
403
+ ```
404
+
405
+ ### Data & Sorting
406
+
407
+ | Prop | Description | Type | Default Value |
408
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
409
+ | preferredCountries | Country codes to show at the top of the list | CountryCode[] | [] |
410
+ | sortCountries | Sort order for countries | 'name' \| 'callingCode' \| 'region' \| 'custom' \| 'none' | 'name' |
411
+ | customSortFunction | Custom sort function | (a: Country, b: Country) => number | undefined |
412
+ | groupByRegion | Group countries by region | boolean | false |
413
+ | showRegionHeaders | Show region headers when grouping | boolean | false |
414
+ | showCurrency | Show currency information | boolean | false |
415
+ | showSubregion | Show subregion information | boolean | false |
416
+
417
+ ### Behavior
418
+
419
+ | Prop | Description | Type | Default Value |
420
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
421
+ | autoFocusSearch | Automatically focus search input when modal opens | boolean | false |
422
+ | closeOnSelect | Close modal when a country is selected | boolean | true |
423
+ | scrollToSelected | Scroll to selected country when modal opens | boolean | false |
424
+ | highlightSelected | Highlight the selected country in the list | boolean | true |
425
+ | selectedItemBackgroundColor | Background color for selected item | string | undefined |
426
+ | selectedItemTextColor | Text color for selected item | string | undefined |
427
+ | emptyStateText | Text to show when no countries found | string | 'No countries found' |
428
+ | emptyStateComponent | Custom component to show when no countries found | React.ReactNode | undefined |
429
+
430
+ ### Performance
431
+
432
+ | Prop | Description | Type | Default Value |
433
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
434
+ | initialNumToRender | Number of items to render initially | number | 10 |
435
+ | maxToRenderPerBatch | Maximum items to render per batch | number | 10 |
436
+ | windowSize | Window size for virtualization | number | 21 |
437
+ | removeClippedSubviews | Remove clipped subviews for better performance | boolean | true |
438
+ | updateCellsBatchingPeriod | Period for batching updates | number | 50 |
439
+
440
+ ### List Configuration
441
+
442
+ | Prop | Description | Type |
443
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|
444
+ | keyExtractor | Custom key extractor for list items | (item: Country, index: number) => string |
445
+ | getItemLayout | Optimize list performance with item layout | (data: Country[] \| null \| undefined, index: number) => { length: number; offset: number; index: number } |
446
+ | ListHeaderComponent | Component to render at the top of the list | React.ComponentType<any> \| React.ReactElement \| null |
447
+ | ListFooterComponent | Component to render at the bottom of the list | React.ComponentType<any> \| React.ReactElement \| null |
448
+ | ListEmptyComponent | Component to render when list is empty | React.ComponentType<any> \| React.ReactElement \| null |
449
+ | ItemSeparatorComponent | Component to render between list items | React.ComponentType<any> \| React.ReactElement \| null |
450
+
451
+ ### Accessibility
452
+
453
+ | Prop | Description | Type |
454
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|
455
+ | accessibilityLabel | Accessibility labels for different elements | Object (see below) |
456
+ | testID | Test IDs for testing | Object (see below) |
457
+
458
+ **accessibilityLabel Object:**
459
+ ```typescript
460
+ {
461
+ container?: string;
462
+ search?: string;
463
+ closeButton?: string;
464
+ listItem?: (country: Country) => string;
465
+ }
466
+ ```
467
+
468
+ **testID Object:**
469
+ ```typescript
470
+ {
471
+ container?: string;
472
+ search?: string;
473
+ closeButton?: string;
474
+ modal?: string;
475
+ list?: string;
476
+ }
477
+ ```
478
+
479
+ ### Advanced Styling
480
+
481
+ | Prop | Description | Type | Default Value |
482
+ | :--------------------:|:-------------------------------------------------------------------------------------------:|:-----------------------------:|:-------------:|
483
+ | theme | Theme mode (light/dark/auto) | 'light' \| 'dark' \| 'auto' | 'light' |
484
+ | rippleColor | Android ripple effect color | string | undefined |
485
+ | activeOpacity | Opacity when pressed | number | 0.7 |
486
+ | underlayColor | Underlay color for pressable (iOS) | string | undefined |
487
+ | loading | Show loading state | boolean | false |
488
+ | loadingComponent | Custom loading component | React.ReactNode | undefined |
489
+
490
+ ### Colors Object
491
+
492
+ ```typescript
493
+ {
494
+ grayLight?: string; // Default: '#E5E5E5'
495
+ white?: string; // Default: '#FFFFFF'
496
+ grayBackground?: string; // Default: '#F5F5F5'
497
+ gray?: string; // Default: '#999999'
498
+ dark?: string; // Default: '#000000'
499
+ }
500
+ ```
501
+
502
+ ## Examples
503
+
504
+ ### Basic Usage
505
+
506
+ ```jsx
507
+ <CountryPicker
508
+ countryCode={countryCode}
509
+ onSelect={(code, country) => {
510
+ setCountryCode(code);
511
+ console.log('Selected:', country.name?.common);
512
+ console.log('Calling Code:', country.callingCode[0]);
513
+ console.log('Currency:', country.currency[0]);
514
+ console.log('Region:', country.region);
515
+ }}
516
+ />
517
+ ```
518
+
519
+ ### Accessing Full Country Details
520
+
521
+ The `onSelect` callback provides both the country code and the complete country object:
522
+
523
+ ```jsx
524
+ <CountryPicker
525
+ countryCode={countryCode}
526
+ onSelect={(code, country) => {
527
+ // country object contains:
528
+ // - countryCode: CountryCode
529
+ // - name: { common: string, ... }
530
+ // - callingCode: string[]
531
+ // - currency: string[]
532
+ // - region: string
533
+ // - subregion: string
534
+ // - flag: string (URL)
535
+
536
+ setCountryCode(code);
537
+ console.log('Full country details:', country);
538
+ }}
539
+ />
540
+ ```
541
+
542
+ ### Custom Colors
543
+
544
+ ```jsx
545
+ <CountryPicker
546
+ countryCode={countryCode}
547
+ onSelect={(code, country) => setCountryCode(code)}
548
+ colors={{
549
+ grayLight: '#D0D0D0',
550
+ white: '#FAFAFA',
551
+ grayBackground: '#F0F0F0',
552
+ gray: '#888888',
553
+ dark: '#333333',
554
+ }}
555
+ headerText="Select Country"
556
+ searchPlaceholder="Find your country"
557
+ iconColor="#007AFF"
558
+ />
559
+ ```
560
+
561
+ ### Filter Countries
562
+
563
+ ```jsx
564
+ <CountryPicker
565
+ countryCode={countryCode}
566
+ onSelect={(code, country) => setCountryCode(code)}
567
+ includedCountries={['US', 'CA', 'GB', 'AU']} // Only show these countries
568
+ // OR
569
+ excludedCountries={['CN', 'RU']} // Exclude these countries
570
+ />
571
+ ```
572
+
573
+ ### Custom Display Options
574
+
575
+ ```jsx
576
+ <CountryPicker
577
+ countryCode={countryCode}
578
+ onSelect={(code, country) => setCountryCode(code)}
579
+ showFlag={true}
580
+ showCallingCode={true}
581
+ showCountryName={false}
582
+ flagSize={32}
583
+ enableSearch={true}
584
+ />
585
+ ```
586
+
587
+ ### Custom Icons
588
+
589
+ ```jsx
590
+ <CountryPicker
591
+ countryCode={countryCode}
592
+ onSelect={(code, country) => setCountryCode(code)}
593
+ dropdownIconName="arrow-drop-down"
594
+ closeIconName="close"
595
+ searchIconName="search"
596
+ checkIconName="check-circle"
597
+ iconColor="#007AFF"
598
+ />
599
+ ```
600
+
601
+ ### Custom Render Functions
602
+
603
+ ```jsx
604
+ <CountryPicker
605
+ countryCode={countryCode}
606
+ onSelect={(code, country) => setCountryCode(code)}
607
+ renderSelectedCountry={(country) => (
608
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
609
+ <Text>{country.name.common}</Text>
610
+ <Text style={{ marginLeft: 8 }}>+{country.callingCode[0]}</Text>
611
+ </View>
612
+ )}
613
+ renderListItem={(country, isSelected, onPress) => (
614
+ <TouchableOpacity onPress={onPress}>
615
+ <Text>{country.name.common}</Text>
616
+ {isSelected && <Text>✓</Text>}
617
+ </TouchableOpacity>
618
+ )}
619
+ />
620
+ ```
621
+
622
+ ### Custom Search Filter
623
+
624
+ ```jsx
625
+ <CountryPicker
626
+ countryCode={countryCode}
627
+ onSelect={(code, country) => setCountryCode(code)}
628
+ customSearchFilter={(item, searchText) => {
629
+ // Search by country name or calling code
630
+ const nameMatch = item.name.common.toLowerCase().includes(searchText.toLowerCase());
631
+ const codeMatch = item.callingCode.some(code => code.includes(searchText));
632
+ return nameMatch || codeMatch;
633
+ }}
634
+ />
635
+ ```
636
+
637
+ ### With Callbacks
638
+
639
+ ```jsx
640
+ <CountryPicker
641
+ countryCode={countryCode}
642
+ onSelect={(code, country) => {
643
+ setCountryCode(code);
644
+ console.log('Selected:', code);
645
+ console.log('Country Name:', country.name?.common);
646
+ console.log('Calling Code:', country.callingCode[0]);
647
+ }}
648
+ onOpen={() => console.log('Picker opened')}
649
+ onClose={() => console.log('Picker closed')}
650
+ onSearch={(text, filtered) => {
651
+ console.log('Search:', text, 'Results:', filtered.length);
652
+ }}
653
+ />
654
+ ```
655
+
656
+ ### Typography & Sizing
657
+
658
+ ```jsx
659
+ <CountryPicker
660
+ countryCode={countryCode}
661
+ onSelect={(code, country) => setCountryCode(code)}
662
+ fontSize={{
663
+ callingCode: 18,
664
+ countryName: 16,
665
+ header: 20,
666
+ search: 16,
667
+ listItem: 15,
668
+ }}
669
+ fontWeight={{
670
+ callingCode: '600',
671
+ countryName: '500',
672
+ header: '700',
673
+ }}
674
+ containerHeight={60}
675
+ containerWidth="100%"
676
+ flagSize={32}
677
+ />
678
+ ```
679
+
680
+ ### Spacing & Layout
681
+
682
+ ```jsx
683
+ <CountryPicker
684
+ countryCode={countryCode}
685
+ onSelect={(code, country) => setCountryCode(code)}
686
+ spacing={{
687
+ containerPadding: 16,
688
+ flagMargin: 12,
689
+ callingCodeMargin: 8,
690
+ headerPadding: 20,
691
+ searchMargin: 16,
692
+ listItemMargin: 8,
693
+ listItemPadding: 12,
694
+ }}
695
+ borderRadius={{
696
+ container: 12,
697
+ flag: 16,
698
+ search: 8,
699
+ listItem: 6,
700
+ modal: 0,
701
+ }}
702
+ />
703
+ ```
704
+
705
+ ### Border & Shadow
706
+
707
+ ```jsx
708
+ <CountryPicker
709
+ countryCode={countryCode}
710
+ onSelect={(code, country) => setCountryCode(code)}
711
+ borderWidth={{
712
+ container: 2,
713
+ search: 1,
714
+ listItem: 0,
715
+ }}
716
+ shadow={{
717
+ container: true,
718
+ modal: true,
719
+ listItem: false,
720
+ }}
721
+ elevation={{
722
+ container: 4,
723
+ modal: 8,
724
+ listItem: 2,
725
+ }}
726
+ />
727
+ ```
728
+
729
+ ### Data & Sorting
730
+
731
+ ```jsx
732
+ <CountryPicker
733
+ countryCode={countryCode}
734
+ onSelect={(code, country) => setCountryCode(code)}
735
+ preferredCountries={['US', 'CA', 'GB', 'AU']}
736
+ sortCountries="callingCode"
737
+ // OR
738
+ sortCountries="custom"
739
+ customSortFunction={(a, b) => {
740
+ // Custom sorting logic
741
+ return a.name.common.localeCompare(b.name.common);
742
+ }}
743
+ showCurrency={true}
744
+ showSubregion={true}
745
+ />
746
+ ```
747
+
748
+ ### Behavior Customization
749
+
750
+ ```jsx
751
+ <CountryPicker
752
+ countryCode={countryCode}
753
+ onSelect={(code, country) => setCountryCode(code)}
754
+ autoFocusSearch={true}
755
+ closeOnSelect={true}
756
+ scrollToSelected={true}
757
+ highlightSelected={true}
758
+ selectedItemBackgroundColor="#E3F2FD"
759
+ selectedItemTextColor="#1976D2"
760
+ emptyStateText="No matching countries"
761
+ />
762
+ ```
763
+
764
+ ### Performance Optimization
765
+
766
+ ```jsx
767
+ <CountryPicker
768
+ countryCode={countryCode}
769
+ onSelect={(code, country) => setCountryCode(code)}
770
+ initialNumToRender={20}
771
+ maxToRenderPerBatch={15}
772
+ windowSize={30}
773
+ removeClippedSubviews={true}
774
+ updateCellsBatchingPeriod={100}
775
+ getItemLayout={(data, index) => ({
776
+ length: 60,
777
+ offset: 60 * index,
778
+ index,
779
+ })}
780
+ />
781
+ ```
782
+
783
+ ### Accessibility
784
+
785
+ ```jsx
786
+ <CountryPicker
787
+ countryCode={countryCode}
788
+ onSelect={(code, country) => setCountryCode(code)}
789
+ accessibilityLabel={{
790
+ container: "Select country",
791
+ search: "Search countries",
792
+ closeButton: "Close country picker",
793
+ listItem: (country) => `Select ${country.name.common}`,
794
+ }}
795
+ testID={{
796
+ container: "country-picker",
797
+ search: "country-search",
798
+ closeButton: "close-button",
799
+ modal: "country-modal",
800
+ list: "country-list",
801
+ }}
802
+ />
803
+ ```
804
+
805
+ ### Advanced Styling
806
+
807
+ ```jsx
808
+ <CountryPicker
809
+ countryCode={countryCode}
810
+ onSelect={(code, country) => setCountryCode(code)}
811
+ theme="dark"
812
+ rippleColor="#6200EE"
813
+ activeOpacity={0.8}
814
+ underlayColor="#F5F5F5"
815
+ loading={isLoading}
816
+ loadingComponent={<ActivityIndicator size="large" />}
817
+ />
818
+ ```
819
+
820
+ ### Complete Example with All Features
821
+
822
+ ```jsx
823
+ <CountryPicker
824
+ countryCode={countryCode}
825
+ onSelect={(code, country) => setCountryCode(code)}
826
+ // Display
827
+ showFlag={true}
828
+ showCallingCode={true}
829
+ showCountryName={false}
830
+ flagSize={32}
831
+ enableSearch={true}
832
+
833
+ // Styling
834
+ colors={{
835
+ grayLight: '#E0E0E0',
836
+ white: '#FFFFFF',
837
+ grayBackground: '#F5F5F5',
838
+ gray: '#9E9E9E',
839
+ dark: '#212121',
840
+ }}
841
+ fontSize={{
842
+ callingCode: 18,
843
+ header: 20,
844
+ }}
845
+ spacing={{
846
+ containerPadding: 16,
847
+ flagMargin: 12,
848
+ }}
849
+ borderRadius={{
850
+ container: 12,
851
+ flag: 16,
852
+ }}
853
+
854
+ // Data
855
+ preferredCountries={['US', 'CA', 'GB']}
856
+ sortCountries="name"
857
+ showCurrency={true}
858
+
859
+ // Behavior
860
+ autoFocusSearch={true}
861
+ highlightSelected={true}
862
+ selectedItemBackgroundColor="#E3F2FD"
863
+
864
+ // Performance
865
+ initialNumToRender={15}
866
+ removeClippedSubviews={true}
867
+
868
+ // Callbacks
869
+ onOpen={() => console.log('Opened')}
870
+ onClose={() => console.log('Closed')}
871
+ onSearch={(text, results) => console.log(`Found ${results.length} countries`)}
872
+ />
873
+ ```
874
+
875
+ ## TypeScript Support
876
+
877
+ The package is written in TypeScript and includes type definitions. Import types as needed:
878
+
879
+ ```typescript
880
+ import CountryPicker from '@avaiyakapil/react-native-country-picker';
881
+ import { CountryCode, Country } from '@avaiyakapil/react-native-country-picker';
882
+ ```
883
+
884
+ ### Type Definitions
885
+
886
+ - `CountryCode`: Union type of all supported country codes (ISO 3166-1 alpha-2)
887
+ - `Country`: Country object type with the following structure:
888
+ ```typescript
889
+ {
890
+ countryCode?: CountryCode;
891
+ currency: string[];
892
+ callingCode: string[];
893
+ region: string;
894
+ subregion: string;
895
+ flag: string; // Flag emoji URL
896
+ name: {
897
+ common: string;
898
+ // ... other language translations
899
+ };
900
+ }
901
+ ```
902
+
903
+ ## Common Questions
904
+
905
+ ### How to get country code?
906
+ ```jsx
907
+ onSelect={(code, country) => {
908
+ console.log('Country Code:', code); // e.g., 'US', 'GB', 'CA'
909
+ }}
910
+ ```
911
+
912
+ ### How to get calling code?
913
+ ```jsx
914
+ onSelect={(code, country) => {
915
+ console.log('Calling Code:', country.callingCode[0]); // e.g., '1', '44', '1'
916
+ }}
917
+ ```
918
+
919
+ ### How to filter countries?
920
+ ```jsx
921
+ <CountryPicker
922
+ includedCountries={['US', 'CA', 'GB', 'AU']} // Only show these
923
+ // OR
924
+ excludedCountries={['CN', 'RU']} // Exclude these
925
+ />
926
+ ```
927
+
928
+ ### How to customize appearance?
929
+ ```jsx
930
+ <CountryPicker
931
+ colors={{
932
+ grayLight: '#E0E0E0',
933
+ white: '#FFFFFF',
934
+ dark: '#000000',
935
+ }}
936
+ containerHeight={60}
937
+ flagSize={32}
938
+ fontSize={{ callingCode: 18 }}
939
+ />
940
+ ```
941
+
942
+ ## Related Packages
943
+
944
+ - [react-native-phone-number-input](https://www.npmjs.com/package/react-native-phone-number-input) - Complete phone number input with country picker
945
+ - [react-native-country-list](https://www.npmjs.com/package/react-native-country-list) - Country list data
946
+ - [react-native-flags](https://www.npmjs.com/package/react-native-flags) - Country flags component
947
+
948
+ ## Contributing
949
+
950
+ Contributions are welcome! Please feel free to submit a Pull Request.
951
+
952
+ 1. Fork the repository
953
+ 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
954
+ 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
955
+ 4. Push to the branch (`git push origin feature/AmazingFeature`)
956
+ 5. Open a Pull Request
957
+
958
+ ## Support
959
+
960
+ - 📧 Email: [Your Email]
961
+ - 🐛 Issues: [GitHub Issues](https://github.com/avaiyakapil/react-native-country-picker/issues)
962
+ - 💬 Discussions: [GitHub Discussions](https://github.com/avaiyakapil/react-native-country-picker/discussions)
963
+
964
+ ## License
965
+
966
+ ISC
967
+
968
+ ## Author
969
+
970
+ **Kapil Avaiya**
971
+
972
+ - GitHub: [@kapilavaiya](https://github.com/kapilavaiya)
973
+ - Twitter: [@kapilavaiya](https://twitter.com/kapilavaiya)
974
+ - Linkedin: [@kapilavaiya](https://www.linkedin.com/in/kapilavaiya/)
975
+
976
+ ---
977
+
978
+ ⭐ **Star this repo if you find it helpful!**