npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ngx-country-input

v1.0.13

Published

Angular standalone component for country code input with dropdown, validation, and reactive forms support

Downloads

1,160

Readme

🌍 ngx-country-input

Professional Country Code Dropdown for Angular

npm version MIT License Angular 19+ TypeScript Bundle Size

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

Demo Preview


✨ 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/forms

Note: If you already have Angular Material installed, you can skip @angular/material and @angular/cdk.

Option 2: Install Library Only

npm install ngx-country-input

Then ensure these peer dependencies are installed:

npm install @angular/material @angular/cdk @angular/forms

Prerequisites

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 pattern validators in DynamicValidatorConfig handle actual validation
  • Both email and phone patterns must be provided when isDynamicInput is 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.

  1. Use fewer countries by passing a filtered list
  2. Disable flags with [showFlag]="false"
  3. 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.mjs included
  • 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:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. 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


Made with Angular 19 🚀 | Powered by Material Design 🎨 | TypeScript 💙

Star this repo if it helped you!

Report BugRequest Featurenpm Package