customphonenumber
A phone number input backed by intl-tel-input with a country-code flag dropdown and format validation.
A phone number input backed by intl-tel-input with a country-code flag dropdown and format validation. Always use this instead of input with type: "tel".
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
When to use
- Any field that captures a phone number
When NOT to use
Instead of customphonenumber, use… | For… |
|---|---|
input with type: "email" | Email addresses |
customidinput | National ID / document numbers |
Never use input with type: "tel" for phone numbers — use this component.
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
label | string | required | Visible label. Must be unique across the form. |
required | boolean | required | Always false. The expression controls the runtime value. |
defaultRequired | boolean | required | true for required fields, false for optional ones. |
initialCountry | string | optional | ISO 3166-1 alpha-2 country code pre-selected in the flag dropdown (e.g. "rw"). Lowercase. |
preferredCountries | string[] | optional | Country codes pinned to the top of the dropdown (e.g. ["rw"]). |
onlyCountries | string[] | optional | When set, the dropdown shows only these countries. |
excludeCountries | string[] | optional | Country codes removed from the dropdown. |
allowDropdown | boolean | optional | false hides the country flag dropdown and locks the country. |
countryNoValidation | boolean | optional | true validates format against the selected country's rules. false accepts any digit sequence. |
configDetails | object | optional | Alternative to the flat props above using { label, value } objects. See below. |
prefillCountryCodeFrom | string | optional | Key of another form field whose value auto-sets the country code. |
placeholder | string | optional | Ghost text shown inside the field. |
hint | string | optional | Help text rendered below the field. |
readonly | boolean | optional | Renders the field non-editable. |
hideField | boolean | optional | Hides the field and excludes its value. Toggle via expressions["props.hideField"]. |
configDetails (alternative to flat country props)
Use configDetails with { label, value } objects instead of the flat string props:
"configDetails": {
"initialCountry": { "label": "Rwanda", "value": "RW" },
"preferredCountries": [{ "label": "Rwanda", "value": "RW" }],
"onlyCountries": [],
"excludeCountries": []
}Validation
invalidNumberFormat is a named validator and must appear in both validators.validation and validation.messages. Without it in validators.validation, the format message never fires.
Examples
Rwanda-only phone number (standard pattern)
{
"key": "PHONE_NUMBER",
"type": "customphonenumber",
"props": {
"label": "Phone Number",
"placeholder": "Enter phone number",
"required": false,
"defaultRequired": true,
"initialCountry": "rw",
"preferredCountries": ["rw"],
"allowDropdown": false,
"countryNoValidation": true
},
"expressions": {
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "Phone Number is required.",
"invalidNumber": "Provided number is invalid.",
"invalidNumberFormat": "Number format is invalid."
}
},
"validators": {
"validation": ["invalidNumberFormat"]
}
}International phone number with country dropdown
{
"key": "APPLICANT_PHONE",
"type": "customphonenumber",
"props": {
"label": "Phone Number",
"placeholder": "Enter phone number",
"required": false,
"defaultRequired": true,
"allowDropdown": true,
"initialCountry": "rw",
"preferredCountries": ["rw"],
"countryNoValidation": true
},
"expressions": {
"props.required": "!(field?.props?.hideField || field?.hide) && field?.props?.defaultRequired"
},
"validation": {
"messages": {
"required": "Phone Number is required.",
"invalidNumber": "Provided number is invalid.",
"invalidNumberFormat": "Number format is invalid."
}
},
"validators": {
"validation": ["invalidNumberFormat"]
}
}Common mistakes
- Omitting
validators.validation: ["invalidNumberFormat"]— the format message never fires without it. - Using
onlyCountriesandexcludeCountriestogether —onlyCountriesalready defines an allowlist;excludeCountriesis redundant. - Mixing flat string props (
initialCountry: "rw") andconfigDetailsobjects — pick one format and use it consistently. - Using
inputwithtype: "tel"for phone numbers — always use this component.
Checklist
-
keyisUPPER_SNAKE_CASEand unique across the entire form -
props.labelis present, unique, and correctly cased - Field is nested at the correct depth:
sections > formly-group > block > customphonenumber -
required: falseis set (nevertrue) -
defaultRequiredis set -
expressions["props.required"]is present with the exact required expression -
validation.messagescontainsrequired,invalidNumber, andinvalidNumberFormat -
validators.validationcontains"invalidNumberFormat" -
initialCountryandpreferredCountriesare set (default to Rwanda:"rw",["rw"]) - Not using
inputwithtype: "tel"anywhere in the form
customtininput
A TIN (Tax Identification Number) input that verifies the number against the RDB/RRA registry and auto-populates downstream fields from the response.
customcurrencyformatinput
A monetary amount input that displays comma-formatted values (e.g. 1,000,000 RWF) while preserving the raw formatted string in the model.