3.5 Optimizing Canvas and Model-Driven App Performance

Key Takeaways

  • The App Checker's Performance tab flags large images, excessive controls, non-delegable queries, and heavy OnStart formulas.
  • Effective pre-loading means minimal App.OnStart plus screen-scoped OnVisible loads, not one large synchronous pull of an entire table.
  • Delegable, filtered queries combined with Concurrent() reduce both server load and canvas app load time simultaneously.
  • Model-driven form performance depends on minimizing visible fields and tabs and disabling default sub-grid data loading.
  • Model-driven view performance depends on limiting displayed columns and filtering or sorting on indexed columns.
Last updated: July 2026

Measuring Before Optimizing

Power Apps Studio includes a built-in App Checker, with a Performance tab that flags specific, actionable issues — large images, excessive controls on a screen, non-delegable queries, and heavy OnStart or OnVisible formulas — rather than leaving developers to guess. The blueprint's "optimize canvas app performance" sub-topic (3e) centers on two named techniques — pre-loading data and query delegation — plus a broader set of practices that reduce the amount of work the app does on load and per interaction.

Pre-Loading Data the Right Way

"Pre-loading" doesn't mean pulling every table into a giant App.OnStart collection — that pattern is a common anti-pattern that increases startup time because the app blocks on a large synchronous data pull before the first screen even renders. The recommended pattern instead:

  • Keep App.OnStart minimal — only global settings, user context, or small lookup tables genuinely needed everywhere.
  • Load screen-specific data in that screen's OnVisible, not centrally, so the cost is paid only when a user actually visits the screen.
  • Where data really should be cached client-side (for speed or offline use), use ClearCollect against a delegable, filtered query rather than the full table, and wrap independent loads in Concurrent() so they run in parallel instead of one after another.
  • Use Concurrent() broadly for any set of OnStart or OnVisible calls that don't depend on each other's results — it's one of the highest-value fixes the App Checker's Performance tab typically surfaces.

Delegation as a Performance Lever

Delegation is also fundamentally a performance concern: a delegable Filter or Sort pushes work to the server and returns only the rows needed, while a non-delegable formula silently caps results at the delegation limit and does the filtering client-side after retrieving up to that many rows — slower and less correct at scale. Raising the delegation limit setting is not a fix; it increases the number of rows pulled locally rather than solving the underlying non-delegable expression, trading a larger payload for the same fundamental bottleneck.

Reducing Control Count and Screen Complexity

Beyond data, rendering cost scales with the number of controls and rules a screen must evaluate:

TechniqueEffect
Minimize total visible controls per screenFewer property formulas to re-evaluate on every state change
Avoid nested galleries where a flat structure worksReduces per-row control multiplication
Hide, don't duplicate, near-identical screensFewer app-wide OnVisible and data triggers
Compress or resize images before import; prefer SVG for iconsSmaller app payload, faster load
Limit the number of distinct data sources referenced by one screenFewer connection handshakes on screen load

Formulas referencing Self or Parent excessively, or deeply nested If/Switch chains recalculated on every keystroke, add up the same way — the Performance tab flags screens whose combined control and formula cost crosses platform-recommended thresholds.

Model-Driven Form Performance

Model-driven apps optimize differently because forms are metadata-driven rather than formula-driven. Key levers for "optimize model-driven app performance (forms and views)" (3f):

  • Minimize fields and tabs on the main form — every visible field, especially those bound to formatted or calculated columns or lookups, adds retrieval and rendering cost; move rarely-used fields to a secondary tab that loads only when clicked.
  • Turn off default data loading for sub-grids and related-record tabs, so they lazy-load only when a user actually expands them, instead of running a query for every related entity on every form open.
  • Limit business rules and synchronous JavaScript on form load — each adds sequential client-side execution time before the form is interactive; asynchronous patterns and event-scoped handlers that only fire on the field or save events they actually need outperform broad on-load scripts that touch every field.
  • Use quick view forms sparingly — each one issues its own retrieve against the related record.

Model-Driven View Performance

Views are essentially FetchXML queries rendered as grids, so view performance follows normal query-optimization logic:

  • Limit displayed columns to what users need; every column adds to the query and render cost, especially formatted lookup or option-set columns.
  • Filter and sort on indexed columns. Dataverse automatically indexes primary keys and some system columns; sorting or filtering large tables on non-indexed custom columns is slow at scale, and heavily-filtered custom columns are good candidates for requesting a custom index.
  • Avoid unnecessary related-entity columns in a view, since each pulls from a joined table.
  • Cap default record counts sensibly and rely on server-side paging rather than large page sizes that pull excess rows the user won't scroll to.

Across both app types, the underlying principle is the same one the blueprint tests repeatedly: retrieve only what's needed, only when it's needed, and let the server, not the client, do filtering and sorting whenever delegation or indexing makes that possible.

Test Your Knowledge

A canvas app's App.OnStart collects an entire 40,000-row Orders table into a local collection before the first screen renders. What is the most accurate assessment of this pattern?

A
B
C
D
Test Your Knowledge

A model-driven app's main form is slow to open because every sub-grid and related-record tab queries its data immediately on load. What is the recommended fix?

A
B
C
D