What it does
A set of Power Fx habits that decide whether an app feels instant or feels broken: what you pull into memory, when calculations run, how many round trips a screen makes, and whether work happens in parallel. Most canvas performance problems are formula problems, not platform problems.
Key facts
- Explicit Column Selection is on by default for new apps and trims retrieved columns to the ones the app actually uses. Lineage can be lost when data passes through collections, and ShowColumns after a collection reference is the documented way to force it back.
- Named formulas in App.Formulas let Power Fx schedule the work itself instead of doing everything in OnStart, and breaking a long script into named formulas is Microsoft's own first suggestion for load time.
- With, Set and UpdateContext create collections internally. That's why they break delegation — and why wrapping a big query in With doesn't do what people assume.
- Concurrent runs formulas at the same time. Microsoft describes the speed-up as modest, and warns that dependent operations run concurrently cause timing and throttling problems.
ForAll( x, Collect( y, ... ) )notifies every rule that depends onyon each iteration. Inverting it toCollect( y, ForAll( x, ... ) )evaluates those rules once.- Reading Gallery.AllItems generates a new output table every time. Gallery.AllItemsCount is the cheap way to get a count.
- Dataverse thumbnails are around a kilobyte and stored inline with the record. Full images and all SharePoint images need a separate call and shouldn't sit in a gallery or table.
- DelayOutput on a text input introduces a one-second pause before the change is detected, which collapses a search-per-keystroke into a search-per-phrase.
- Flexible-height galleries need a sensible TemplateSize default. Setting it to 0 makes the platform try to render everything.
- The non-blocking OnStart rule is the default: a screen can render and become interactive before App.OnStart or Screen.OnVisible finishes.
When to use / skip
Treat "collect everything into memory on start" as the anti-pattern it is, not as a delegation workaround. It's the single most common cause of a canvas app that takes thirty seconds to open, and it gets worse every month as the data grows. Query directly, delegate, and let paging do its job. Collections are for genuinely small reference data, for offline scenarios, and for staging a batch of writes — not for avoiding a delegation warning. The other rule worth holding to: measure with Monitor before optimising, because the formula everyone blames is rarely the expensive one.
Configuration decisions
- What actually needs to be in memory at launch, versus loaded when a screen becomes visible.
- Which values become named formulas so the platform can defer them, and which genuinely need OnStart.
- Where Concurrent is worth the risk, given dependent calls can throttle each other.
- Whether repeated lookups get hoisted into a named formula, cached in a small collection, or left to recalculate.
- How images are handled: thumbnails in galleries, full images behind an explicit user action.
Gotchas
- With looks like a performance optimisation and is frequently used as one. It creates an in-memory record, so anything inside it is evaluated locally and won't delegate.
- Wrapping a data source in a collection loses column lineage, which quietly disables Explicit Column Selection and pulls back more data than before you "optimised" it.
- Non-blocking OnStart means variables you initialise there may not be set when another rule reads them. Apps that used to work by accident break when the switch is on.
- Concurrent around calls that depend on each other produces intermittent, unreproducible failures. They'll be reported as random.
- The count of controls on a screen matters as much as the formulas. A screen with hundreds of controls is slow regardless of how tight the Power Fx is.
Consultant notes
- Get Monitor in front of the client's makers early. Performance arguments based on a trace are won; performance arguments based on best practice are argued about for a sprint.
- The three changes with the best return on a slow inherited app are almost always: move OnStart work to named formulas, remove the "collect everything" pattern, and check Explicit Column Selection is on and not being defeated by collections.
- Push back on performance requirements expressed as a number without a data volume attached. "Under two seconds" means nothing until you know how many rows and how many columns.
- Warn the client that performance decays. An app that was fine at 400 records behaves differently at 40,000, and nobody rebudgets for that unless you flag it at design time.
Worth revisiting if the default row limits change, or when new load-time features land in a release wave.