Mastering Phone Input Masks in Power Apps: A Flexible Mask System
Create custom input masks for phone numbers and other patterns using a dynamic configuration table and real-time formatting.
Standard canvas app text inputs do not support automatic input masking — a feature that prevents invalid characters and inserts separators like hyphens or spaces as the user types. This is especially valuable for phone numbers, where country‑specific patterns (e.g., (###) ###-#### for the US, +## #### ###### for the UK) greatly improve data quality and user experience.
While Power Apps lacks a built‑in mask control, we can build a flexible mask engine using a simple workaround. This article walks through a reusable method that lets you define masks per country and apply formatting in real time.
Scenario: International Partner Contact Form
Your organisation works with suppliers from different regions. You need a single form where the user selects a country and, as they start typing the phone number, the placeholder automatically formats according to that country’s standard. The system must allow easy updates – a non‑developer should be able to change masks later.
Imagine a Partner Details screen with these controls:
| Control | Name | Purpose |
|---|---|---|
| Dropdown | ddlCountry | Choose a country (e.g., US, UK, Japan) |
| Text Input | txtPhone | Where the formatted phone number appears |
| Slider | sldTracker | Hidden control that triggers formatting after each keystroke |
| Label | lblFormat | Shows the current mask pattern for reference (hidden in production) |
We will store the mask patterns in a collection that can be populated from a SharePoint list or environment variable for easy remote updates.
Step 1: Configure the mask patterns
First we create a collection that maps each country code to its desired mask. A mask is a string of characters where # represents a digit and all other characters are inserted verbatim.
Open the App.OnStart property and add the following code.
ClearCollect(
colMasks,
{Country: "US", Mask: "(###) ###-####"},
{Country: "UK", Mask: "+## #### ######"},
{Country: "Japan", Mask: "(##)-####-####"},
{Country: "India", Mask: "+## ####-######"}
);
Set(
gblSelectedCountry,
First(colMasks)
);In a production app, you might pull this data from a SharePoint list named PhoneMasks and use Collect(colMasks, Filter(PhoneMasks, Active=true)).
Step 2: Build the mask parser
To apply formatting, we need to know, for each digit position in the incoming phone number, what the corresponding visible mask pattern is. We parse the mask string once and store the result in a collection.
Create a new screen and add the controls listed in the scenario above. In the OnSelect property of a button (or directly in App.OnStart), we run the parser whenever the selected country changes.
/* Build a table of mask "chunks": one row per digit position,
containing the cumulative mask string and the count of # seen so far */
ClearCollect(
colMaskDigits,
With(
{varMask: LookUp(colMasks, Country = ddlCountry.Selected.Value).Mask},
With(
{
varSeq: ForAll(
Sequence(Len(varMask)),
With(
{varLeft: Left(varMask, Value)},
{
Pos: Value,
PartialMask: varLeft,
DigitCount: CountRows(MatchAll(varLeft, "#"))
}
)
)
},
ForAll(
Sequence(Max(varSeq, DigitCount)),
LookUp(varSeq, DigitCount = Value)
)
)
)
);The resulting table colMaskDigits has one row per expected digit position (e.g., for (###) ###-#### there are 10 rows). Each row contains the cumulative mask string PartialMask and the DigitCount up to that point.
Step 3: Real‑time formatting with a slider workaround
Power Apps text inputs only fire OnChange when the control loses focus, not after every keystroke. To format as the user types, we use an invisible slider whose Default property is tied to the length of txtPhone.Text. The slider’s OnChange then fires immediately on each character change.
Set the slider’s Default property:
Len(txtPhone.Text)
Now write the core formatting logic in the slider’s OnChange:
/* Extract only digit characters from the text */
Set(
gblDigitsOnly,
Concat(
MatchAll(txtPhone.Text, Match.Digit),
FullMatch
)
);
/* Apply the mask by formatting the number string as a numeric value
using the mask from the colMaskDigits table for the current length */
Set(
gblFormatted,
With(
{pos: Len(gblDigitsOnly)},
If(
pos > 0,
Text(
Value(gblDigitsOnly),
LookUp(colMaskDigits, DigitCount = pos, PartialMask)
),
""
)
)
);
/* Force the text input to show the formatted value */
Reset(txtPhone);Finally, set txtPhone.Default to gblFormatted. Because we call Reset inside the slider’s OnChange, the text input is re‑evaluated with the new formatted string. This does not cause an infinite loop because the reset clears the input and triggers the slider again only if the length changes, but by then the digits only variable is unchanged if the user hasn’t typed more. The technique works reliably.
Step 4: Limit input length
Set txtPhone.MaxLength to the length of the selected mask string. This prevents the user from exceeding the number of available digit slots.
Len(LookUp(colMasks, Country = ddlCountry.Selected.Value).Mask)
If you want to enforce that only digits are typed, you can also handle OnChange of the text input to strip non‑digit characters, but the mask already does that – any non‑digit keystroke will be ignored because the slider’s Default doesn’t change (Len stays the same). To be safe, consider adding a quick validation:
If(
CountRows(MatchAll(Self.Text, Match.Digit)) < Len(Self.Text),
Notify("Only numbers are allowed", NotificationType.Warning)
)Step 5: Hide helper controls
In production, the slider, country dropdown, and mask label can be tucked away:
sldTracker.Visible = falselblFormat.Visible = falseddlCountrycan be kept visible or hidden if the country is determined from another source.
The final screenshot should show the txtPhone control gracefully formatting as numbers are entered.
Performance and delegation notes
- The mask parser uses
ForAllover the length of the mask (usually fewer than 20 characters). This is negligible and does not hit delegation limits. - The
MatchAllfunction in the formatting logic processes only the current text input length, which is also trivial. - If you store masks in a SharePoint list, be aware that the
LookUpinside the slider fires on every keystroke. Keep the mask list small (under 500 items) or cache the selected mask into a variable. - For large‑scale apps, consider moving the mask definition to an environment variable to reduce network calls.
Common mistakes and troubleshooting
- Slider not advancing: Ensure the slider’s
Defaultis bound toLen(txtPhone.Text). If the slider is invisible, test with a visible label to confirm the length is changing. - Format resets to empty: The
Reset(txtPhone)insideOnChangewill clear the input ifgblFormattedis empty. Always check thatgblDigitsOnlyis not blank before formatting. - Backspace deletes formatting wrongly: The current method re‑applies the mask based on digit count, so deleting a digit correctly removes the last digit and reformats. That works because
Len(gblDigitsOnly)decreases. - Country change causes errors: When the user picks a different country, re‑run the parser (Step 2) and also clear
gblFormattedandtxtPhone.Textto avoid stale digits.
Final recommendation
While this workaround is effective, consider packaging it into a reusable component (a Canvas Component) that encapsulates the slider, the formatting logic, and the mask configuration. Export the component as a solution for reuse across multiple apps. For enterprise scenarios, also consider adding a fallback to a custom control built with PCF if you need full cross‑browser compatibility and advanced features like cursor‑aware editing.
The approach shown here gives a flexible, easy‑to‑maintain input mask system that works for any country or arbitrary pattern – from tax IDs to postal codes. By externalising the mask definitions, you empower non‑developers to manage formatting changes without touching app logic.
References
- Original source: Power Apps Phone Number Formatting For Any Country (Input Mask) by Matthew Devaney
- Microsoft Learn: MatchAll and Match functions
- Microsoft Learn: Create a canvas component
Learn how small coding practices—like using descriptive names, flattening conditions, and simplifying logic—can make your apps easier to update and less error-prone.
Move past the gallery and discover the hidden patterns for validation, navigation, and smart submission handling in your data entry forms.
PowerShell unlocks admin capabilities that the Power Platform admin center simply doesn’t offer—from recovering deleted apps to blocking trial licenses. Here’s how to wield them safely.