Your Power App looks amazing, great! But if it’s dog slow and not very performant, you’ll struggle with long-term adoption and users will get frustrated. There are lots of Power Apps performance considerations we can implement when building apps.
In this article I’ll go over my recommended practices to ensure a smooth and efficient experience for users.
Table of Contents
ToggleData and testing
For the purposes of examples in this article, I’ve drafted two tables of dummy data. I’ve replicated the tables in both Dataverse and SharePoint:
1- a table/list of 10 Star Wars characters.
2- a table/list of 5,000 Star Wars battles that have happened across the galaxy.
Where possible, I’ll try to show impact for both data sources.
A simple way to test Power Apps performance is to set up some timers. We can use these to test different methodologies or patterns and see the time it takes to complete. You can use this method for debugging and identifying performance blockers in your Power Fx.
To begin, add a button to your app. In the OnSelect property, add the following:
UpdateContext({cvTimerStart: Now()});
This will create a timestamp for when the processing started. After the semi-colon, add the Power Fx you want to analyse for speed and performance. Then, add the following to stop the processing and return the overall duration:
UpdateContext({cvTimerEnd: DateDiff(cvTimerStart, Now(), TimeUnit.Milliseconds)})
Add a label to the app and set the Text property to the TimerEnd variable. It might be good to concatenate with the metric you’ve chosen, for me that’s milliseconds:
If you want to analyse two blocks of Power Fx, side by side, remember to use different variable names for each. For example cvTimerStart01 and cvTimerEnd01, versus cvTimerStart02 and cvTimerEnd02.
The tips below aren’t in any particular order. Where possible, I’ll evidence any gains with some of the techniques. These might be minimal in their own right, but adding the gains together will be very important for your Power Apps performance.
Use collections
At times we may need to reference frequently used data in an app. A typical pattern is to add a data connection and use lookups, sums, filters and so on. Each time we do this directly against a connection, we are calling that API.
Instead, consider storing the data in collections. This will store the data in memory for the duration of the app’s session. Perform lookups and calculations against the collection instead.
However…
Keep data retrievals small
Just because your data source has 200,000 rows of data, it doesn’t mean you need to load it all into memory. Keep in mind the Power Apps performance implications of doing so.
Only load the columns you need
Building collections with SharePoint lists will load every single column – even ones you didn’t even know were there!
// Build battles collection
ClearCollect(colBattles, Battles)
Here’s a sample of some of those columns. We’re storing this in memory for each app session:
Instead, use the ShowColumns function to target only the columns you need:
// Build battles collection
ClearCollect(
colSPOBattles,
ShowColumns(
Battles,
// Specific columns to retrieve:
Title,
'Character Name',
'Weapons Used',
'Battle Name',
'Battle Type',
Planet
)
);
Here’s the impact on Power Apps performance:
Use the Concurrent function
The concurrent function has been around for some time and is a huge help for performance. Rather than processing commands one at a time, we can group them together to run in parallel.
In the following example, I’m building separate collections based on the Battles SharePoint list, one for each Planet. This is not using concurrency:
// Collections built one by one
ClearCollect(colCoruscant, Filter(Battles, Planet="Coruscant"));
ClearCollect(colHoth, Filter(Battles, Planet="Hoth"));
ClearCollect(colJakku, Filter(Battles, Planet="Jakku"));
ClearCollect(colNaboo, Filter(Battles, Planet="Naboo"));
ClearCollect(colScarif, Filter(Battles, Planet="Scarif"));
ClearCollect(colTatooine, Filter(Battles, Planet="Tatooine"));
Let’s see the same logic but using the concurrent function:
// Collections built simultaneously
Concurrent(
ClearCollect(colCoruscant, Filter(Battles, Planet="Coruscant")),
ClearCollect(colHoth, Filter(Battles, Planet="Hoth")),
ClearCollect(colJakku, Filter(Battles, Planet="Jakku")),
ClearCollect(colNaboo, Filter(Battles, Planet="Naboo")),
ClearCollect(colScarif, Filter(Battles, Planet="Scarif")),
ClearCollect(colTatooine, Filter(Battles, Planet="Tatooine"))
);
A simple change but yields a decent Power Apps performance gain:
Avoid N+1
The N+1 problem relates to making too many network/API calls. This commonly occurs when performing lookups to external data sources in galleries. This ties back to using collections for reference data instead.
In the example below, I have a gallery hooked up to my Star Wars Battles SharePoint list. I’ve added a label to the gallery, performing a lookup to the Characters list to get their allegiance:
When scrolling through the gallery, look at the impact on Power Apps performance in Monitor:
Let’s do the same lookup to a collection:
Huge improvement:
Alternatively, use the AddColumns function to leverage the data you need. You can leverage this within the Items property of a gallery:
AddColumns(
// Battles SharePoint list
Battles,
// Added column name to reference in gallery
Allegiance,
// Lookup to Characters SharePoint list
LookUp(
Characters,
Title = 'Character Name'
).Allegiance
)
Don't overload OnStart
We’ve all been guilty of this one at least once, I know I have! Loading in SharePoint lists, pre-setting variables, theming, checking logged-on user permissions – all common scenarios we’d previously put in the OnStart function of an app.
As you’d expect, the more Power Fx that’s here, the more of an impact on Power Apps performance. Carefully plan what actually needs to be loaded on the apps start and what can be shifted elsewhere. For example, using a screens OnVisible property instead, or consider…
Named Formulas
Named formulas were made generally available in 2023 and are a real game changer for Power Apps performance and scalability. Unlike code in OnStart, Named Formulas are always available and therefore, the results are always up to date.
As explained by Microsoft, it’s actually a concept brought over from Excel. Most of those candidates for OnStart can easily be switched over to Named Formulas. Check out my article for some common use cases and how to implement them.
Views instead of Filters
If you’re building on top of SharePoint, chances are you’re building a lot of filtered data sets using Power Fx. Naturally, this is going to increase the ask on your app. Here’s a typical example, filtering my Battles list where the character is either Chewy or Luke:
ClearCollect(
colBattlesChewyLuke,
Filter(
Battles,
'Character Name' = "Chewbacca" || 'Character Name' = "Luke Skywalker"
)
);
Dataverse or SQL users can take advantage of Views. These process filtered data at source, meaning all the work has already been done on the ‘server’ side. We can point our Power Apps directly to the views (pre-aggregated data) and save a bunch of processing time.
I’ve added a view to the Battles Dataverse table with the following filters applied:
With the Dataverse table added as a connection to the app, we can then use Power Fx to target the view directly:
ClearCollect(
colBattlesChewyLuke,
Filter(
'Star Wars Battles',
'Star Wars Battles (Views)'.'Chewy and Luke'
)
);
As seen below, another decent performance gain:
Going back to the Avoid N+1 tip earlier, this is REALLY easy to avoid with views. Simply add the related fields into the view instead!
Use components for reusable content
There’s many scenarios where you might have the same controls copied onto lots of screens. Navigation menu’s, headers, footers and so on.
Each control used in an app is a tiny piece of code. When a user plays an app, each bit of code needs to be compiled and presented. The more controls, the more there is to compile and present, so the more your Power Apps performance will be impacted.
Consider wrapping duplicated controls into components. A component is considered as a single control in an app or custom page. You have a couple of options for this:
1- Building in-app components. these can only be used within the context of the app.
2- Building a component library. These can be used in any app or custom page within the same environment.
3- Using pre-built component libraries. Examples here are the Creator Kit or the Power Apps Design Toolkit.
Don't ram your app full of controls
Even with components sorted, a canvas app can still be heavily bloated with too many controls. There used to be a suggested limit of 500 controls per app to maintain a good level of optimisation. I’m not sure if that’s still a thing, but it’s a good guide to keep in mind.
Here’s a simple example of where you can reduce number of controls. Let’s say you have 5 buttons on a screen that act as a menu or filter. Why not add the options to a collection, then leverage the collection as a gallery. Add a button control for the selectable item, this reduces 5 controls down to 2.
If you’re still struggling to keep controls to a sensible number, consider multiple apps. Need to count how many controls your app has? Easy – use the Code Review tool. The App Overview screen will give you a breakdown of the number of controls per screen.
Streamline If functions
You may need to evaluate a value in Power Fx that has a conditional outcome. There could be multiple outcomes and you need to set a variable, or colour a value based on what condition is met. A common approach is to use multiple If statements, sometimes referred to as nested If statements.
With(
{tvData:
LookUp(Battles,Title = "SWBT-0000").Planet},
If(
tvData = "Coruscant",
UpdateContext({cvColour: Color.Red}),
If(
tvData = "Hoth",
UpdateContext({cvColour: Color.Blue}),
If(
tvData = "Jakku",
UpdateContext({cvColour: Color.Green}),
If(
tvData = "Naboo",
UpdateContext({cvColour: Color.Yellow}),
If(
tvData = "Scarif",
UpdateContext({cvColour: Color.Orange}),
If(
tvData = "Tatooine",
UpdateContext({cvColour: Color.DimGrey})
)
)
)
)
)
)
);
Besides from being quite clunky on the eye, it’s not hugely great for performance either. There are two ways you can improve the logic to aid Power Apps performance.
Option 1 is to streamline your If statement. You can list the conditions without starting a new, nested If each time:
With(
{tvData:
LookUp(Battles,Title = "SWBT-0000").Planet},
If(
tvData = "Coruscant",
UpdateContext({cvColour: Color.Red}),
tvData = "Hoth",
UpdateContext({cvColour: Color.Blue}),
tvData = "Jakku",
UpdateContext({cvColour: Color.Green}),
tvData = "Naboo",
UpdateContext({cvColour: Color.Yellow}),
tvData = "Scarif",
UpdateContext({cvColour: Color.Orange}),
tvData = "Tatooine",
UpdateContext({cvColour: Color.DimGrey})
));
Option 2 is to use the Switch function instead:
With(
{tvData:
LookUp(Battles,Title = "SWBT-0000").Planet},
Switch(
tvData,
"Coruscant",
UpdateContext({cvColour: Color.Red}),
"Hoth",
UpdateContext({cvColour: Color.Blue}),
"Jakku",
UpdateContext({cvColour: Color.Green}),
"Naboo",
UpdateContext({cvColour: Color.Yellow}),
"Scarif",
UpdateContext({cvColour: Color.Orange}),
"Tatooine",
UpdateContext({cvColour: Color.DimGrey})
)
);
I’ve previously blogged about If vs Switch functions, as the performance gains can be sizeable. The below shows comparisons of nested If vs streamlined If vs Switch:
You only need one Form Control
Something I’ve seen a lot over the years is apps that have 3 form controls; one to create a record, one to edit a record and one to view a record. If the data source is always the same, you only need a single form.
A form will use four controls per field/column, so you’ll burn through the recommended number of controls per app very quickly. Having three form controls perhaps isn’t the best idea for Power Apps performance.
I’ve covered form controls previously, especially around using a single form control for all three operations (create, edit, view). If you have hundreds of columns to capture data for, consider dynamic forms instead of using a form control.
Remove unused media and connections
Removing unused media files and data connections can give a Power Apps performance boost too. In recent times, this has been made easier with a new ‘quality of life’ improvement. Simply click on the broom icon in each area to automatically remove anything not in use:
First Filter vs Lookup
You may have a requirement for getting the first record from a filtered set of data. A common pattern to get this information is to combine the First and Filter functions.
In the example below, I want to get the first record relating to Boba Fett, then get the Battle name:
UpdateContext(
{
cvBobaFettBattle: First(
Filter(
Battles,
'Character Name' = "Boba Fett"
)
).'Battle Name'
}
);
Unfortunately, the First/Filter approach isn’t the best for Power Apps performance. Consider using the Lookup function instead. This will get the first value it finds based on the condition:
UpdateContext(
{
cvBobaFettBattle: LookUp(
Battles,
'Character Name' = "Boba Fett"
).'Battle Name'
}
);
Another good technique to be aware of to keep milking those performance gains:
Avoid screen transitions
I know they look cool, especially if you’re new to Power Apps. Seeing the screen slide in from the left or right, without writing any code, is pretty cool. But navigating to another screen with a screen transition can be painful for some.
Spare a thought for any users of the app who aren’t able to get amazing wifi or cellular speeds, or whose device is a bit old. In my experience, screen transitions can appear laggy and frustrating for them.
My default for some time has been to use no screen transitions when navigating screens. You can achieve this simply by leaving the screen transition property out of the Navigate function completely, as the default is ScreenTransition.None:
Navigate('Home Screen')
Periodically republish your apps
If you’ve read the Power Apps coding standards white paper, you may have seen this paragraph:
When you republish an app, it updates to and runs on the latest version of Power Apps. This means it’ll get the benefit of any performance upgrades released since it was last published.
This is easy enough to monitor in Power Automate. Using the Get App or Get Apps action, you can see the apps latest published version. If this is older than x number of days, fire off an alert to relevant parties or perform other actions. You can even directly republish an app in Power Automate if you want to.
Delay outputs
Delay outputs is a very useful property for text input controls (classic and modern). When set to true, any user input is registered after a half a second delay. This is great for delaying any expensive operations until a user has finished inputting their text.
To enable for a classic text input control, simply select the text input control and find the DelayOutput property:
For a modern text input control, you’ll need to update the Trigger Output property to Delayed. At time of writing the modern text input control is still in preview, so not yet ready for your production apps.
Use delegable functions
Delegation is a big deal when it comes to Power Apps performance. It determines what our Power Fx can and can’t do when it comes to retrieving information from a data source.
Delegable function: data processing is delegated to the data source (ie SharePoint or Dataverse).
Non-delegable function: data processing is performed locally (as in the users device).
For the latter, this is why there’s a default of limit of 500 records that can be processed. You can increase this to 2,000 if you must, in the Settings area of your app.
Delegation is a key concept to understand, which you can read more about it here. Additional info on what functions are delegable for SharePoint and Dataverse may prove useful reads.
Do the operation once only
Another common hindrance for Power Apps is performing multiple lookups or filters to get different values from a record.
In the example below, I’m setting four separate variables. Each one is using a lookup to my list of Battles where the character is Boba Fett.
UpdateContext(
{
cvBattleName: LookUp(
Battles,
'Character Name' = "Boba Fett").'Battle Name',
cvBattleType: LookUp(
Battles,
'Character Name' = "Boba Fett").'Battle Type',
cvCharacter: LookUp(
Battles,
'Character Name' = "Boba Fett").'Character Name',
cvWeaponsUsed: LookUp(
Battles,
'Character Name' = "Boba Fett").'Weapons Used'
}
);
Repeating the lookup is an expensive operation. You could store the data in a collection if you’ll need to continually reference it in your app. However, this approach also consumes memory. If you just need to set the variables, consider using the With function instead. You can reference data temporarily – without storing it in memory – to get the values you need:
With(
{
tvData: LookUp(
Battles,
'Character Name' = "Boba Fett"
)
},
// Set some variables
UpdateContext(
{
cvBattleName: tvData.'Battle Name',
cvBattleType: tvData.'Battle Type',
cvCharacter: tvData.'Character Name',
cvWeaponsUsed: tvData.'Weapons Used'
}
)
);
More Power Apps performance benefits as a result:
Avoid cross-screen dependencies
Who here has added a gallery on one screen and a form control on another?
Who here has used Gallery.Selected in the Items property of the form?
Yep, me too 🤣. At least, I used to when I first started building canvas apps.
The issue with this multi-screen approach is screen dependencies. For Power Apps to operate this functionality, it has to load BOTH screens and keep them in memory. This might not be the most performant of techniques.
Option 1 is to save the selected gallery item as a global variable, then reference the variable in the Items property of the form control. This avoids the cross-screen dependency, but we then have a global variable stored in memory instead.
Option 2 is to save the selected gallery item as a context variable, then pass it through to the form screen within a Navigate function. This is only stored within the context of the operation:
Navigate(
'My Form Screen',
ScreenTransition.None,
{cvSelectedItem: ThisItem}
)
The cross-screen impact will be the same if you’re referring any control or property on another screen, such as X or Y properties or a text input value. Be mindful of performance when doing so.
Patch multiple rows the RIGHT way
One of the biggest culprits for Power Apps performance is how you update multiple rows of data. Typically this is done with the Patch and ForAll functions, but it’s how they’re used that’s important.
The most common approach used is embedding the Patch within a ForAll, thus creating a loop functionality. This will process a single record at a time, so imagine what that does for 100 or so records.
// Patch all rows in collection using ForAll(Patch) approach
ForAll(
colBattles,
Patch(
'Star Wars Battles',
Defaults('Star Wars Battles'),
{
BattleID: myBattleID,
BattleName: myBattleName,
BattleType: myBattleType,
WeaponsUsed: myWeaponsUsed
}
)
);
Simply swapping the Patch and ForAll around will yield so much performance benefit:
// Patch all rows in collection using Patch(ForAll) approach
Patch('Star Wars Battles',
ForAll(colBattles,
{
BattleID: myBattleID,
BattleName: myBattleName,
BattleType: myBattleType,
WeaponsUsed: myWeaponsUsed
}
)
);
Unfortunately, there’s quite a bit of misinformation out there for patching multiple rows in Power Apps. Even popular AI engines will recommend the former approach:
One of the most popular articles I’ve done to date walks you through three techniques for patching multiple rows. Do take a look and take your Patch game to the next level.
Limit use of nested galleries
You may need to present data in a hierarchical way. Nested galleries are usually the go-to way of visualising this in your canvas apps. The downside is the additional processing overhead required to do this.
You can sometimes reduce the need for nested galleries by structuring your data more efficiently (see the nested nav menu technique as an example). If you must nest, ensure your data is saved as collections or named formulas.
SVGs > other image formats
SVG’s, or Scalable Vector Graphics are a super lightweight and flexible option for presenting images and icons in Power Apps.
One of the huge benefits is not even having to add the media file to your app, SVG’s can be referenced as code within Image controls. The code instructs how to draw the image rather than storing pixel data, resulting in much faster load times. You can also do a lot of cool stuff with SVG’s such as animations and background transparency.
You can find all sorts of information via Google and Copilot for SVGs in Power Apps. If you want to really see some cool stuff, go check out Robin Rosengrün’s Fun with SVGs YouTube playlist. Prepare to be amazed.
Move heavy processing to Power Automate
Ever heard the saying “just because you can, it doesn’t mean you should“? It’s easy to get carried away in a canvas app, writing all sorts of Power Fx to crunch calculations and handle epic amounts of data processing. Yes, I used to be one of those people! 😆
Consider what operations in your apps are, or could be background tasks. If the user doesn’t need to see or interact with the outputs, these are prime candidates for shifting to Power Automate instead. This keeps your Power Apps more lightweight and performant.
Use the tools available
It can be difficult to keep pace with the rate of change and the impact on Power Apps performance. This isn’t going to help when we’re up against a tight deadline and trying to remember every single performance trick. Thankfully, there are some tools available to us to help keep us honest.
1- Test yourself: At the top of this article, I highlighted a quick technique to analyse how long it takes for Power Fx to run. Run these tests over and over, at different times of the day, on different devices. Get confidence that your Power Fx is efficient no matter what scenario.
2- Code Review Tool: Quite a few of the tips above will be outputs from a code review. Bake one into every solution deployment cycle to ensure you’ve not missed anything obvious.
3- Combine the Trace function and Monitor: Continuous monitoring of the performance of productionised apps is key to ongoing adoption. Weave in the Trace function into your Power Fx logic and understand how it works with the Power Apps monitor tool.
4- Browser dev tools: Each browser has its own developer tools console. Use this to analyse performance and understand any potential bottlenecks.
Use Dataverse
For my final tip, use Dataverse. I appreciate it’s not that simple for some organisations but it’s far superior compared to most other data sources (looking at you, SharePoint).
Dataverse offers much more in terms of performance and scalability, with better support for delegation too. With some of the examples shown above, performance is better with Dataverse even for the non-performant methods.
And don’t get me started on the better security options either, but that’s for another article!
What are your must-know tips for increasing Power Apps performance? If you know any I’ve not listed above, please let me know in the comments.
If you liked this article and want to receive more helpful tips about the Power Platform, don’t forget to subscribe or follow me on socials 😊. Thanks for reading.
Another fantastic article! I really appreciate these concise segments, especially with the direct links to specific topics. Thanks a lot!
Love the tip using views instead of filtering. Great article as always. Thanks for sharing
Great article, could you please share the dummy data? I would liketo do some testing, thanks
Hi Eduardo, I simply used Copilot to generate some csv outputs that I used for testing!
Nice article Craig