@fagon/ngx-intl-phone
v1.0.1
Published
A fully-accessible Angular component for international phone number input with real-time validation, formatting, and theming—supporting all countries, multiple layouts (split/group/stack), CSS variable customization, Reactive Forms integration, and detail
Maintainers
Readme
IntlPhoneNumber
Overview
NgxIntlPhone is a robust, highly-configurable Angular component for international phone number input, validation, and formatting. It supports all countries, integrates seamlessly with Angular Reactive Forms, and provides three beautiful layouts in a single, easy-to-use API.
- 📱 All-in-one: Country selection, phone validation, formatting, and error display
- 🌍 Global: Supports every country and dial code
- 🎨 Flexible: Three layouts (
split,group,stack) for any UI - 🧩 Angular-native: Works with standalone components and NgModules
- 🧪 Reactive Forms: Implements
ControlValueAccessorfor form integration - 🛡️ Accessible: Keyboard navigation, ARIA attributes, and focus management
- 🎛️ Customizable: Theming via CSS variables, configurable labels, and more
Installation
npm install @fagon/ngx-intl-phonePeer Dependencies
@angular/core,@angular/common,@angular/forms(v16+)libphonenumber-js(for validation/formatting)i18n-iso-countries(for country names)
Quick Start
1. Import the Module
import { IntlPhoneModule } from "@fagon/ngx-intl-phone";
@NgModule({
imports: [IntlPhoneModule]
})
export class AppModule {}Or, for standalone components:
import { IntlPhoneComponent } from "@fagon/ngx-intl-phone";2. Basic Usage
<intl-phone [(ngModel)]="phone"></intl-phone>
<!-- or with Reactive Forms -->
<intl-phone [formControl]="phoneControl"></intl-phone>3. Import Styles
The component ships with encapsulated styles that work out of the box in most setups. However, if your application enforces a strict Content Security Policy (CSP) — particularly one that blocks inline styles (style-src 'unsafe-inline') — the component's encapsulated styles may be suppressed by the browser.
In that case, import the pre-built stylesheet globally instead:
In angular.json:
"styles": [
"node_modules/@fagon/ngx-intl-phone/styles.css",
"src/styles.css"
]Or in your global stylesheet (src/styles.scss / src/styles.css):
@import "@fagon/ngx-intl-phone/styles.css";When to use this: Enable this import if you see the component rendering without borders, layout, or dropdown styling under a strict CSP. It is also recommended for Angular Universal (SSR) projects, where component-encapsulated styles may not be applied during server-side rendering.
Layouts
Switch between three layouts using the layout input:
- split (default): Country selector and input side-by-side
- group: Unified input with divider
- stack: Vertically stacked fields with labels
<!-- Split (default) -->
<intl-phone></intl-phone>
<intl-phone layout="split"></intl-phone>
<!-- Group -->
<intl-phone layout="group"></intl-phone>
<!-- Stack (labels required) -->
<intl-phone
layout="stack"
countryLabel="Country"
phoneLabel="Phone Number"
></intl-phone>Inputs
| Input | Type | Default | Description |
| ------------------ | ------------------------------- | ---------------------- | ------------------------------------------------------------------------------------ |
| layout | 'split' \| 'group' \| 'stack' | 'split' | Layout style for the input |
| countryLabel | string | 'Country' | Label for country selector (required for stack layout) |
| phoneLabel | string | 'Phone Number' | Label for phone input (required for stack layout) |
| placeholder | string | 'Enter phone number' | Input placeholder |
| defaultCountry | string (ISO2 or name) | 'GH' | Default selected country (ISO2 code or full name, e.g. 'GH' or 'Ghana') |
| allowedCountries | string[] (ISO2 codes) | undefined | Restrict selectable countries (e.g. ["GH", "NG", "KE"]). All countries if omitted. |
| config | IntlPhoneConfig | See below | Theming and behavior overrides |
Available Countries
By default, all countries are available. To restrict the dropdown, use allowedCountries:
<intl-phone [allowedCountries]="['GH', 'NG', 'KE', 'ZA']"></intl-phone>Default Country
Set the default country by ISO2 code or full name:
<intl-phone defaultCountry="NG"></intl-phone>
<intl-phone defaultCountry="Nigeria"></intl-phone>If the value is not found, the first country in the list is used.
Practical Examples
Restrict to West Africa
<intl-phone
[allowedCountries]="['GH', 'NG', 'CI', 'SN', 'GM']"
defaultCountry="GH"
[(ngModel)]="phone"
></intl-phone>Custom Labels and Placeholder
<intl-phone
layout="stack"
countryLabel="Select Country"
phoneLabel="Mobile Number"
placeholder="e.g. 024 123 4567"
[(ngModel)]="phone"
></intl-phone>Reactive Forms with Allowed Countries
form = new FormGroup({
phone: new FormControl("", Validators.required)
});<form [formGroup]="form">
<intl-phone
formControlName="phone"
[allowedCountries]="['GH', 'NG', 'KE']"
defaultCountry="KE"
></intl-phone>
</form>Outputs
| Output | Type | Description |
| ------------- | ----------------------- | --------------------------------------- |
| validChange | EventEmitter<boolean> | Emits validity state when phone changes |
Types
IntlPhoneConfig
export interface IntlPhoneConfig {
borderColor?: string;
borderColorHover?: string;
borderColorFocus?: string;
borderColorInvalid?: string;
borderColorValid?: string;
borderRadius?: string;
inputHeight?: string;
dropdownMaxHeight?: string;
fontSize?: string;
fontFamily?: string;
}Default Config
export const DEFAULT_INTL_PHONE_CONFIG: Required<IntlPhoneConfig> = {
borderColor: "#e9ecef",
borderColorHover: "#b5afa8",
borderColorFocus: "#e9ecef",
borderColorInvalid: "#e90b0b",
borderColorValid: "#198754",
borderRadius: "12px",
inputHeight: "44px",
dropdownMaxHeight: "264px",
fontSize: "14px",
fontFamily: "inherit"
};Country
export interface Country {
name: string;
iso2: string;
dialCode: string;
flag: string;
}Works with both template-driven and reactive forms:
form = new FormGroup({
phone: new FormControl("", Validators.required)
});<form [formGroup]="form">
<intl-phone formControlName="phone"></intl-phone>
</form>Theming & Customization
Override any CSS variable to match your design system:
intl-phone {
--phone-height: 48px;
--phone-border: #e0e0e0;
--phone-border-focus: #1976d2;
--phone-border-invalid: #d32f2f;
--phone-border-valid: "#198754";
--phone-radius: 8px;
--phone-font-size: 16px;
--phone-font-family: "Inter", sans-serif;
}Note: CSS variable overrides apply regardless of whether you are using component-encapsulated styles or the global
styles.cssimport. See Import Styles for guidance on which approach suits your setup.
Advanced Configuration
You can provide a global config using the module:
IntlPhoneModule.forRoot({
borderRadius: "8px",
borderColorFocus: "#1976d2",
inputHeight: "48px"
});Or per-component:
<intl-phone
[config]="{ borderColor: '#333', inputHeight: '40px' }"
></intl-phone>Accessibility
- Full keyboard navigation
- ARIA attributes for dropdown and input
- Focus ring and invalid state styling
Example
<intl-phone
layout="group"
[placeholder]="'Your mobile number'"
[defaultCountryName]="'Nigeria'"
[config]="{
borderColor: '#bdbdbd',
borderColorFocus: '#1976d2',
borderRadius: '6px'
}"
(validChange)="onPhoneValid($event)"
[(ngModel)]="phone"
></intl-phone>License
MIT © Fagon Technologies
Contributing
PRs welcome! Please open an issue to discuss your proposal before submitting.
Support
For questions, issues, or feature requests, please open an issue on GitHub.
Enjoy building with IntlPhoneNumber!
