ngx-country-input
v1.0.13
Published
Angular standalone component for country code input with dropdown, validation, and reactive forms support
Downloads
1,160
Maintainers
Readme
🌍 ngx-country-input
Professional Country Code Dropdown for Angular
A powerful, feature-rich Angular 19+ component for international phone number and email input with intelligent country code selection and validation.
✨ Features • 📦 Installation • 🚀 Quick Start • 📖 Documentation • 💡 Examples • 🔧 API
✨ Features
🎨 Rich UI/UX
- 🎯 Material Design integration
- 🚩 Visual flags for 244 countries
- 🔍 Real-time search in dropdown
- 📱 Fully responsive design
- ♿ Accessible form controls
⚙️ Powerful Validation
- ✉️ Dynamic email/phone detection
- 🔒 Built-in validators (required, pattern, length)
- 🎭 Custom validators support
- 📝 Priority-based error messages
- 🌐 Country-specific formatting
🌍 Global Coverage
- 🗺️ 244 countries included
- 🌎 All regions covered worldwide
- 📞 Accurate dial codes
- 🏳️ ISO 3166-1 alpha-2 codes
- 🔧 Custom country lists
🚀 Developer Experience
- 📦 Standalone component (Angular 19/20)
- 💪 Full TypeScript support
- ⚡ Reactive Forms integration
- 🎯 Multiple instances supported
- 📚 Comprehensive docs
📦 Installation
Option 1: Install with Peer Dependencies (Recommended)
Install the library along with all required peer dependencies in one command:
npm install ngx-country-input @angular/material @angular/cdk @angular/formsNote: If you already have Angular Material installed, you can skip
@angular/materialand@angular/cdk.
Option 2: Install Library Only
npm install ngx-country-inputThen ensure these peer dependencies are installed:
npm install @angular/material @angular/cdk @angular/formsPrerequisites
Ensure your project has these peer dependencies:
{
"@angular/core": ">=19.0.0 <21.0.0",
"@angular/material": ">=19.0.0 <21.0.0",
"@angular/cdk": ">=19.0.0 <21.0.0",
"@angular/forms": ">=19.0.0 <21.0.0",
"rxjs": "^7.0.0 || ^8.0.0"
}Compatible with Angular 19 and Angular 20 ✅
⚠️ Important: npm does not automatically install peer dependencies. You must install them manually using one of the options above.
Setup Material Theme
Add to your styles.scss:
@import '@angular/material/prebuilt-themes/indigo-pink.css';
// For country flags
.flagstrap-icon {
background-image: url('/node_modules/ngx-country-input/src/lib/assets/flags.png');
}🚀 Quick Start
Basic Usage
import { Component } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { CountryCodeInputComponent } from 'ngx-country-input';
@Component({
selector: 'app-contact-form',
standalone: true,
imports: [ReactiveFormsModule, CountryCodeInputComponent],
template: `
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
<ngx-country-input
[formGroup]="contactForm"
[controlName]="'phone'"
[floatingLabelName]="'Phone Number'"
[formValidators]="{ required: true, minLength: 7, maxLength: 15 }"
[showSearch]="true"
[showFlag]="true" />
<button type="submit" [disabled]="contactForm.invalid">Submit</button>
</form>
`,
})
export class ContactFormComponent {
contactForm = new FormGroup({});
onSubmit() {
if (this.contactForm.valid) {
console.log(this.contactForm.value);
// Output: { phone: '+1-5551234567' }
}
}
}That's it! You now have a fully functional international phone input with:
- ✅ Country code dropdown with flags
- ✅ Phone number validation
- ✅ Search functionality
- ✅ Reactive forms integration
💡 Examples
🎯 Dynamic Email or Phone Validation
Enable automatic detection of email OR phone input with intelligent UI adaptation:
import { DynamicValidatorConfig } from 'ngx-country-input';
@Component({
template: `
<ngx-country-input
[formGroup]="myForm"
[controlName]="'emailOrPhone'"
[floatingLabelName]="'Email or Phone'"
[isDynamicInput]="true"
[formValidators]="dynamicValidator" />
`,
})
export class MyComponent {
myForm = new FormGroup({});
dynamicValidator: DynamicValidatorConfig = {
required: true,
minLength: 7,
maxLength: 50,
pattern: {
email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
phone: /^[0-9()-\s]{7,15}$/,
},
};
}🔍 How Dynamic Detection Works
When isDynamicInput is enabled, the component automatically detects input type:
Default Detection Pattern:
/^[0-9()-]{7,14}$/; // Matches: 7-14 digits with optional () and -Behavior:
- Input matches pattern → Detected as phone → Country code dropdown shown ✅
- Input doesn't match → Detected as email → Country code dropdown hidden ❌
Examples:
"1234567" → Phone (shows country code)
"(555) 123-4567" → Phone (shows country code)
"[email protected]" → Email (hides country code)
"abc123" → Email (hides country code)Important Notes:
- The default pattern only triggers UI behavior (show/hide country code)
- Your custom
patternvalidators inDynamicValidatorConfighandle actual validation - Both email and phone patterns must be provided when
isDynamicInputis true
🎨 Customize Detection Pattern
Override the default detection pattern to match your specific use case:
@Component({
template: `
<ngx-country-input
[formGroup]="myForm"
[controlName]="'contact'"
[isDynamicInput]="true"
[dynamicRegex]="customPhoneDetection"
[formValidators]="dynamicValidator" />
`,
})
export class MyComponent {
myForm = new FormGroup({});
// Custom detection: allows spaces and up to 16 digits
customPhoneDetection = /^[0-9()\-\s]{7,16}$/;
dynamicValidator: DynamicValidatorConfig = {
required: true,
pattern: {
email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
phone: /^[0-9()-\s]{7,16}$/, // Match your detection pattern
},
};
}Use Cases:
// Allow longer phone numbers (up to 20 digits)
dynamicRegex = /^[0-9()-\s]{7,20}$/;
// Stricter - only digits, no special chars
dynamicRegex = /^[0-9]{7,15}$/;
// Allow plus sign for international format
dynamicRegex = /^[\+0-9()-\s]{7,16}$/;🎨 Custom Error Messages with Priority
Control which error appears first:
import { FieldErrorMessages } from 'ngx-country-input';
errorMessages: FieldErrorMessages = {
required: {
message: 'Phone number is required',
preferenceLevel: 1,
},
pattern: {
message: 'Please enter a valid phone number',
preferenceLevel: 2,
},
minlength: {
message: 'Phone must be at least 7 digits',
preferenceLevel: 3,
},
};<ngx-country-input
[formGroup]="myForm"
[formValidators]="{ required: true, minLength: 7, pattern: /^[0-9()-\s]{7,15}$/ }"
[errorMessages]="errorMessages" />🌍 Custom Country Lists
Use specific countries or filter by region:
import { SAMPLE_COUNTRIES, getCountryByCode } from 'ngx-country-input';
@Component({
template: `
<ngx-country-input
[formGroup]="myForm"
[countryDataInput]="customCountries" />
`
})
export class MyComponent {
// Option 1: Handpicked countries
customCountries = [
getCountryByCode('US')!,
getCountryByCode('IN')!,
getCountryByCode('GB')!,
getCountryByCode('CA')!
];
// Option 2: Filter by region
asianCountries = SAMPLE_COUNTRIES.filter(c =>
['IN', 'CN', 'JP', 'KR', 'SG', 'MY', 'TH'].includes(c.id)
);
// Option 3: Search by name
import { searchCountries } from 'ngx-country-input';
unitedCountries = searchCountries('united');
// Returns: [United States, United Kingdom, United Arab Emirates]
}🎯 Set Default Country Code
Pre-select a default country in the dropdown:
import { getCountryByCode } from 'ngx-country-input';
@Component({
template: `
<!-- Default to United States -->
<ngx-country-input [formGroup]="myForm" [controlName]="'phone'" [floatingLabelName]="'Phone Number'" [defaultCountry]="getCountryByCode('US')!" />
<!-- Default to United Kingdom -->
<ngx-country-input
[formGroup]="myForm"
[controlName]="'mobile'"
[floatingLabelName]="'Mobile'"
[defaultCountry]="{ id: 'GB', name: 'United Kingdom', dialCode: '+44' }" />
`,
})
export class MyComponent {
myForm = new FormGroup({});
getCountryByCode = getCountryByCode;
}Note: Without specifying defaultCountry, the component defaults to India (+91).
🎭 Custom Validators
Add your own validation logic:
import { ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
// No consecutive repeated digits
const noConsecutiveDigits: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) return null;
const hasConsecutive = /(.)\1{2,}/.test(value);
return hasConsecutive ? { consecutiveDigits: { value: control.value } } : null;
};<ngx-country-input
[formGroup]="myForm"
[formCustomValidator]="[noConsecutiveDigits]"
[errorMessages]="{
consecutiveDigits: {
message: 'Too many repeated digits',
preferenceLevel: 3
}
}" />📱 Multiple Contact Fields
Use multiple inputs in the same form:
@Component({
template: `
<form [formGroup]="contactsForm">
<!-- Primary Phone -->
<ngx-country-input
[formGroup]="contactsForm"
[controlName]="'primaryPhone'"
[floatingLabelName]="'Primary Phone'"
[formValidators]="{ required: true }" />
<!-- Secondary Phone (Optional) -->
<ngx-country-input [formGroup]="contactsForm" [controlName]="'secondaryPhone'" [floatingLabelName]="'Secondary Phone (Optional)'" />
<!-- Email or Phone -->
<ngx-country-input
[formGroup]="contactsForm"
[controlName]="'emailOrPhone'"
[floatingLabelName]="'Email or Phone'"
[isDynamicInput]="true"
[formValidators]="dynamicValidator" />
</form>
`,
})
export class ContactsComponent {
contactsForm = new FormGroup({});
dynamicValidator: DynamicValidatorConfig = {
required: true,
pattern: {
email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
phone: /^[0-9()-\s]{7,15}$/,
},
};
}🎧 Listen to User Input
Track changes in real-time:
@Component({
template: ` <ngx-country-input [formGroup]="myForm" (userTypedValue)="onInputChange($event)" /> `,
})
export class MyComponent {
onInputChange(value: string) {
console.log('User typing:', value);
// Perform real-time validation, API calls, suggestions, etc.
}
}🎛️ Customize UI Elements
<ngx-country-input
[formGroup]="myForm"
[showSearch]="false" <!-- Disable search -->
[showFlag]="false" <!-- Hide flags -->
[floatingLabelName]="'Contact Number'" />📖 Documentation
🔧 API Reference
Component Inputs
| Input | Type | Default | Description |
| --------------------- | ------------------------------------------- | ---------------------------------------------- | ------------------------------------------------ |
| formGroup | FormGroup | Required | Parent FormGroup to add control to |
| controlName | string | 'emailAndPhone' | Form control name |
| floatingLabelName | string | '' | Label text for input field |
| formValidators | ValidatorConfig \| DynamicValidatorConfig | {} | Built-in validator configuration |
| formCustomValidator | ValidatorFn[] | [] | Array of custom validator functions |
| errorMessages | FieldErrorMessages | {} | Custom error messages with priority |
| isDynamicInput | boolean | false | Enable email/phone auto-detection |
| dynamicRegex | RegExp | /^[0-9()-]{7,14}$/ | Pattern to detect phone vs email for UI behavior |
| defaultCountry | DropDownCountry | { id: 'in', name: 'India', dialCode: '+91' } | Default country to pre-select in dropdown |
| countryDataInput | DropDownCountry[] | SAMPLE_COUNTRIES | Array of country data (244 included) |
| showSearch | boolean | true | Show/hide search in dropdown |
| showFlag | boolean | true | Show/hide flag sprites |
Component Outputs
| Output | Type | Description |
| ---------------- | ---------------------- | ------------------------------------ |
| userTypedValue | EventEmitter<string> | Emitted when user types in the input |
Interfaces
ValidatorConfig
interface ValidatorConfig {
required?: boolean;
maxLength?: number;
minLength?: number;
pattern?: RegExp;
}DynamicValidatorConfig
interface DynamicValidatorConfig {
required?: boolean;
maxLength?: number;
minLength?: number;
pattern: {
email: RegExp;
phone: RegExp;
};
}FieldErrorMessages
interface FieldErrorMessages {
required?: { message: string; preferenceLevel?: number };
minlength?: { message: string; preferenceLevel?: number };
maxlength?: { message: string; preferenceLevel?: number };
pattern?: { message: string; preferenceLevel?: number };
email?: { message: string; preferenceLevel?: number };
phone?: { message: string; preferenceLevel?: number };
[key: string]: { message: string; preferenceLevel?: number } | undefined;
}DropDownCountry
interface DropDownCountry {
id: string; // ISO 3166-1 alpha-2 code (e.g., 'US', 'IN')
name: string; // Full country name (e.g., 'United States')
dialCode: string; // International dialing code (e.g., '+1', '+91')
}🛠️ Helper Functions
import { SAMPLE_COUNTRIES, getCountryByCode, getCountriesByDialCode, searchCountries } from 'ngx-country-input';
// Get all 244 countries
const allCountries = SAMPLE_COUNTRIES;
// Get specific country by ISO code
const india = getCountryByCode('IN');
// Returns: { id: 'IN', name: 'India', dialCode: '+91' }
// Get countries by dial code
const northAmerica = getCountriesByDialCode('+1');
// Returns: [United States, Canada, and other +1 countries]
// Search countries by name
const results = searchCountries('united');
// Returns: [United States, United Kingdom, United Arab Emirates]🌍 Included Countries
| Country | Code | Dial Code | | ----------------- | ---- | --------- | | 🇦🇫 Afghanistan | AF | +93 | | 🇦🇽 Åland Islands | AX | +358 | | 🇦🇱 Albania | AL | +355 | | 🇩🇿 Algeria | DZ | +213 | | 🇦🇸 American Samoa | AS | +1684 | | ... | ... | ... | | 🇿🇲 Zambia | ZM | +260 | | 🇿🇼 Zimbabwe | ZW | +263 |
Complete list of 244 countries spanning all continents and territories.
📊 Bundle Size
Optimized for Production
| Component | Uncompressed | Gzipped | Brotli | | ------------------ | ------------ | ------------- | ------------ | | Main Bundle | ~206KB | ~65-75KB | ~55-65KB | | Flag Sprite | ~28KB | ~25KB | ~23KB | | Total Download | ~234KB | ~90-100KB | ~78-88KB |
What's Inside:
- 📊 244 Countries data: ~35-40KB
- 🚩 244 Flag positions: ~20-25KB
- 🎨 Angular Material: ~40-50KB
- ⚡ RxJS operators: ~30-40KB
- ✅ Validation logic: ~15-20KB
- 🧩 Component code: ~30-40KB
Performance:
- ✅ Tree-shakeable code
- ✅ Lazy-loaded dropdown
- ✅ Optimized flag sprite
- ✅ Minimal runtime overhead
❓ FAQ
No, this library requires Angular 19+ with standalone components. For older versions, you would need to upgrade your Angular project.
- Use fewer countries by passing a filtered list
- Disable flags with
[showFlag]="false" - Disable search with
[showSearch]="false"
// Use only 10 countries instead of 244
const myCountries = SAMPLE_COUNTRIES.filter(c => ['US', 'GB', 'IN', 'CA', 'AU'].includes(c.id));No, the component depends on Angular Material for UI elements (MatFormField, MatInput). Angular Material must be installed.
Yes, compatible with Angular Universal. Ensure provideClientHydration() is in your app.config.ts:
import { provideClientHydration } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [provideClientHydration() /* ... */],
};onSubmit() {
const phoneValue = this.myForm.get('phone')?.value;
console.log('Phone:', phoneValue); // e.g., '+91-9876543210'
}The value includes country code and user input combined.
🐛 Troubleshooting
Flags Not Showing
Problem: Country flags not displaying in dropdown.
Solution: Add flag sprite reference to your global styles.scss:
.flagstrap-icon {
background-image: url('/node_modules/ngx-country-input/src/lib/assets/flags.png');
}Validation Not Working
Problem: Email/phone validation doesn't trigger.
Solution: Ensure isDynamicInput is true and pattern config is correct:
[isDynamicInput]="true"
[formValidators]="{
pattern: {
email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
phone: /^[0-9()-]{7,14}$/
}
}"Material Theme Missing
Problem: Component styling looks broken.
Solution: Import Angular Material theme in styles.scss:
@import '@angular/material/prebuilt-themes/indigo-pink.css';🎨 Styling
Customize appearance by overriding CSS:
// Custom input container
::ng-deep ngx-country-input {
.input-container {
border: 1px solid #ddd;
border-radius: 4px;
}
.country-code-prefix {
background-color: #f5f5f5;
padding: 8px;
}
.flagstrap-icon {
width: 20px;
height: 15px;
}
}
// Material field customization
::ng-deep .mat-mdc-form-field {
width: 100%;
}
::ng-deep .mat-mdc-text-field-wrapper {
background-color: #fafafa;
}📝 Changelog
v1.0.4 (Latest)
- ✅ Optimized npm package - Reduced from 680KB to 338KB
- ✅ Removed old bundle - Only
ngx-country-input.mjsincluded - ✅ Excluded source maps - Not published to npm
- ✅ Updated documentation
v1.0.3
- ✅ Expanded to all 244 countries
- ✅ Enhanced helper functions
- ✅ Improved bundle optimization
v1.0.2
- ✅ Initial npm release with 10 sample countries
- ✅ 244 flag position mappings
- ✅ Complete documentation
v1.0.1
- ✅ Dynamic email/phone validation
- ✅ Custom error messages with priority
- ✅ Searchable country dropdown
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
MIT License - see LICENSE file for details.
🙏 Acknowledgments
Built with ❤️ for the Angular community.
Special thanks to:
- Angular team for the amazing framework
- Angular Material team for beautiful UI components
- All contributors and users of this library
📚 Additional Resources
- Angular Documentation
- Angular Material Documentation
- Reactive Forms Guide
- TypeScript Handbook
- Optimization Guide
Made with Angular 19 🚀 | Powered by Material Design 🎨 | TypeScript 💙
⭐ Star this repo if it helped you! ⭐
