Named Formulas and the With() Function: Power Fx Patterns for Faster Apps
Many canvas apps become slow because every screen carries old habits from the first prototype. OnStart fills variables that may never be used, formulas repeat the same lookups, and controls recalculate more than they need to. Named formulas and With() give you a cleaner way to express intent.
Named formulas make app state declarative
A named formula is a calculation, not a command. You define it in App.Formulas, and Power Fx recalculates it when its dependencies change. It is evaluated only when used, which makes it a strong replacement for many Set calls that run during app startup.
Traditional OnStart logic often tries to initialize everything just in case a screen needs it. That increases startup time and creates stale state problems. Named formulas let the app say what a value is instead of when to set it.
// App.Formulas
CurrentUser = User();
CurrentUserEmail = Lower(CurrentUser.Email);
MyOpenApprovals = Filter(
Approvals,
Approver.Email = CurrentUserEmail && Status = "Pending"
);
IsApprover = !IsBlank(
LookUp(SecurityRoles, Email = CurrentUserEmail && Role = "Approver")
);
When a screen references MyOpenApprovals, the formula is evaluated. If no screen uses it, it does not need to run at startup. That is a better default for large apps.
OnStart should stop pretending to be a data layer
Use OnStart for actions that are truly actions. Authentication side effects, telemetry initialization, feature flag fetches, and one-time setup may still belong there. But values derived from connectors, controls, users, and other formulas usually read better as named formulas.
Before named formulas, a typical app might do too much up front.
// Old App.OnStart pattern
Set(varUser, User());
Set(varUserEmail, Lower(varUser.Email));
Set(
varMyDepartment,
LookUp(Employees, Email = varUserEmail).Department
);
ClearCollect(
colMyRequests,
Filter(Requests, Requester.Email = varUserEmail)
);
Set(varCanApprove, !IsBlank(LookUp(Approvers, Email = varUserEmail)));
The named formula version is more direct and less eager.
// App.Formulas replacement
CurrentUser = User();
CurrentUserEmail = Lower(CurrentUser.Email);
MyEmployeeProfile = LookUp(Employees, Email = CurrentUserEmail);
MyDepartment = MyEmployeeProfile.Department;
MyRequests = Filter(Requests, Requester.Email = CurrentUserEmail);
CanApprove = !IsBlank(LookUp(Approvers, Email = CurrentUserEmail));
This does not mean every collection disappears. Collections are still useful for local editing, offline work, staged rows, and small static reference data. The key is to stop using collections as a reflexive cache for every query.
With() removes repeated work inside a formula
Use With() when a calculation needs local names. It scopes intermediate values to one formula, which keeps the logic readable and avoids repeated lookups. This is especially helpful in display formulas, Patch calls, validation rules, and nested calculations.
Without With(), makers often repeat the same LookUp several times.
// Repeated lookup pattern
If(
LookUp(Accounts, Account = galAccounts.Selected.Account).CreditHold,
"Blocked",
LookUp(Accounts, Account = galAccounts.Selected.Account).'Account Name'
)
With a local value, the formula reads like a small, intentional calculation.
// Scoped value pattern
With(
{
selectedAccount: LookUp(Accounts, Account = galAccounts.Selected.Account)
},
If(
selectedAccount.CreditHold,
"Blocked",
selectedAccount.'Account Name'
)
)
The performance gain depends on context, but the maintainability gain is immediate. If the lookup changes, there is one place to fix it.
Coalesce makes fallback logic boring in the best way
Fallback values should not become nested conditionals. Coalesce returns the first non-blank value. Use it with named formulas and With() to handle optional data, default labels, and missing configuration without sprawling If formulas.
// Display name fallback
With(
{
profile: LookUp(Employees, Email = CurrentUserEmail)
},
Coalesce(
profile.PreferredName,
profile.FullName,
CurrentUser.FullName,
"Unknown user"
)
)
A small pattern like this reduces fragile blank checks across the app. It also makes review easier because the fallback order is explicit.
The migration path is incremental
Do not rewrite the whole app in one pass. Start with read-only variables in OnStart. Anything that looks like Set followed by a formula expression is a candidate. Move it to App.Formulas, update references, and test screens that depend on it.
| Old pattern | Better pattern | Why it helps |
|---|---|---|
Set for current user data | Named formula | Auto-recalculates and avoids startup work |
ClearCollect for large queries | Delegable named formula | Keeps filtering on the data source |
Repeated LookUp in one formula | With() local value | Reduces duplication and clarifies intent |
| Nested blank checks | Coalesce | Makes fallback order explicit |
| Screen variables for derived labels | Control formula or named formula | Removes stale state |
Validate behavior after each move. Named formulas are declarative, so they should not contain behavior that depends on timing. If the original logic relied on an action happening once, keep it as an action or redesign the flow.
The gotchas are mostly about side effects and delegation
Named formulas cannot replace behavior formulas. They should not be used for Patch, Collect, Navigate, or notification side effects. Keep commands in behavior properties such as button OnSelect. Keep values in named formulas.
Delegation still matters. A named formula that filters a large Dataverse table must still be delegable. Moving a bad query from OnStart to App.Formulas improves startup behavior but does not make the query correct at scale.
Also be careful with naming. Use names that read like business concepts: CurrentUserEmail, MyOpenRequests, CanApprove, SelectedAccountRisk. Avoid prefixes that only describe storage mechanics. The point is to make formulas read like the app’s domain.
Review dependencies after migration. A named formula used by many screens becomes a shared contract, so keep it narrow, delegable, and predictable. If a value is expensive and only one screen needs it, keep the formula close to that screen instead of promoting it too early.
Faster apps usually come from less eager work and clearer dependency graphs. Named formulas remove unnecessary startup commands. With() makes local intent obvious. Coalesce makes fallback rules explicit. Together they turn Power Fx from scattered instructions into a maintainable model of the app.
Keep reading
Patch in Canvas Apps: The Blank Column That Overwrites a Real Value
Patch writes every column present in the change record, including blanks from empty form cards. How to update only the fields the user actually edited.
Dataverse Filtered Record Ownership: Row-Level Security Without Owners, Teams, or Sharing
Dataverse's filtered record ownership (preview) secures rows by column values such as region or department instead of owner or business unit. How record filters, entity record filters, and security roles fit together, and what the no-owner model gives up.
Power Platform September 2026 Update: The App-Builder Skill, Agent Plugins, and the Refreshed Model-Driven UI
What matters in Microsoft's September 2026 Power Platform feature update: the model app-builder skill goes GA, the canvas authoring agent plugin goes GA, generative pages embed in forms and reach connectors, model-driven header refresh is GA with display density in preview, and Power Automate gets server-side search.
Canvas Apps: Concurrent() Is How OnStart Stops Feeling Stuck
Load independent Dataverse and SharePoint calls together in a canvas app, and keep the calls that really do depend on each other out of the same Concurrent.
SharePoint Lists vs Dataverse for Canvas Apps: Pick the Storage, Not the Habit
Delegation limits, relationships, security, and ALM — the practical tradeoffs when a canvas app outgrows a list and when it should not migrate at all.
Power Apps Collections vs. Dataverse: Where Your App's Data Should Actually Live
Collections feel fast and convenient, so makers overuse them — and ship apps that lose data and break on refresh. Here is when to hold data in memory and when it belongs in Dataverse.
Newsletter
New posts, straight to your inbox
One email when a new post goes up. Double opt-in, unsubscribe anytime. Not a daily digest.
Comments
- No comments yet. Be the first.