Its the age old question, where do you put your code? Now I suspect you are saying never heard that question before, but it's a choice we actually make.
Everyone knows the client and server choice, as we can run our code in the browser or get the server to do it and send it back through an api. But there is also a more fundamental choice, inline or block.
See you can just add your JavaScript in the element, like this:
<button onclick="alert('Hello World')">Click Me</button>
and I do occasionally see this, but the vast majority of JavaScript has now moved out of inline into consolidate blocks, either in a script tag *(for single page sites) or dedicated JS files:
<script>
document.getElementById("demo").innerHTML = "Hello World";
</script>
So the question we should be asking is why, and bigger question is should we do the same in Power Apps.
In case you missed it Power Apps now has the app formulas properties, that can be used to run reusable code, they are called:
- User Defined Functions (UDF)
- Behaviour UDF's
So let's dive in and explain each
- When to use Inline
- UDF & Behaviour UDF's
- When to use UDF/UDS's
- Functions in Dataveres & Flows
1. When to use Inline
We all know Inline, with each component having 2 main types of properties:
Data - characteristics of the component, like X/Y co-ordinates or font colour
Event - triggered code based on an interaction with the component, like OnClick or OnChange
There are also Function and Action, though these are for custom components or locked code so Im skipping them here
Both Data and Event can run run Power FX code, like setting colour based on a condition or variable, or Patching a table on a OnClick. Code here has one main advantage, it's easy to understand as it is related directly to the component. I can quickly see that If(vsText="Hello World",RGBA(189, 178, 176, 1),Color.Red) will set the fill depending on a variable.
There is also the added advantage of being able to copy and paste the component between apps, as all of the code is self contained.
But there are also trade offs, in particular:
Reviewing Code
Trying to review the code of a Canvas App for either code reviews or debugging is painful when all of the code is inline. There is no one global view of all code, so you have to search and click throughout the app, only ever seeing a small snippet of the code. That advantage of being related to the component falls away when you are tracing interactions across components.
FYI this was such a pain I created a Web App that extracts all of a apps code and displays in one document, check it out at https://appreview-pro.dev
Reusing the Code
A fundamental to writing good code is to reuse your code, that way we our code base is smaller/easier to read, and we maintain code in one place (one version of the truth). With inline code that is simply not possible, so we have to duplicate code.
A good example of this was a app I built a few years back that queried a SQL database, I had to call a custom connector in:
- New Search
- Next Page
- Previous Page
- Jump to Page
- Filter
- Reset filter
- Sort
Each button used the same code but different inputs, but I had to copy and paste the code across all 7 buttons.
So when should we use inline code, well I have 2 rules:
- The Code will never be reused
- One action
The first one is self explanatory, but what about the second. Well its around the code complexity. If the code is doing one action, like logic or maybe a patch, then it can be inline, but if its patching, reseting, and updating, then it should be moved to Source (formulas parameter and called as a function).
2. UDF & Behaviour UDF's
User Defined Functions are exactly what they say on the tin, they are custom functions that you create to run Power FX code. The difference between a standard UDF and a behaviour (not a fan of that name but Microsoft use it, I prefer User Defined Subroutine from my VBA days), are 3 fold:
- Scope
- Trigger
- Outputs
UDF's are scoped to the function, meaning they can interact with variables and components. They are also declarative, which means that the system decides when it wants to recalculate it. Finally UDF's return a value, where as behaviour does not.
Here's a simple example of a UDF
AddOne(num:Number):Number=(
num+1
)
and heres a simple UDS, sorry i mean Behaviour UDF
AddToVar(num:Number):Void ={
Set(viNum,viNum+num)
}
The first returns the passed in value incremented by 1, the second increments a variable by the passed in value.
You create both in the same way:
App-Formulas
But the structure is a little different, with UDF's using () and
functionName( inputName:inputType ) : OutputType (
<Your Code>
)
sorry I'm going to call the UDS now, and UDS using {}
SubroutinenName( inputName:inputType ) : Void {
<Your Code>
}
But call out here, inputs are optional for both and there can be multiple
AddTogether(num:Number,num2:Number):Number=(
num+num2
);
AddOneToVar():Void ={
Set(viNum,viNum+1)
};
As you can see although UDF's are super useful, they are not really going to change the way we code our apps, as the use case is relatively small. But UDS are much more useful as we can follow the path the Web Developers pioneered.
3. When to use UDF/UDS
And this is my hot take, we really should be using UDS's a lot more, in fact looking at our 2 conditions when to use inline, means everything else should be moved to the formulas bar and called as a UDS.
If you plan to reuse the code, then it should be a UDS, if the event property is doing multiple Power FX functions, then it should be a UDS. Now like everything in life there are shades of grey, in particular for the last one, would this have to be moved?
UDS() : Void = {
Patch(dummyData,{ID:1},{Title:"test1"});
ResetForm(Form1);
};
Its actioning multiple Power FX functions, but its only 2 lines of code. And this is where you decide, but for me to create a clear standard and to improve readability I would move it.
But I have the following key caveats:
- Naming must be good, I normally include the component name in it if its not reusable
- If it is reusable include which components use it in comments
4. Functions in Dataveres & Flows
Back at the start I also talked about client and server, so thought it would be good to also bring them into my decision matrix.
Functions in Dataverse use Power FX, but they run on the server (I've done a lot more in-depth into these in a full blog here: Instant Dataverse Functions & Low-Code Plug-ins). The main reason to use Functions in Dataverse is for reusability, as the code is behind a simple API url, so can be called from other apps, flows and even pro-code solutions. But even for those use cases you have to consider:
- At time of print its still very buggy and Microsoft's seems to have lost focus
- Requires Premium license
- Max run time is 2 minutes
- It is synchronous, so you have to wait for the function to finish
So these would purely be used for those niche cases where the code needs to be run across multiple platforms.
The other server side is calling a flow, this adds the complexity of another tech (not Power FX), but if we are honest every Power App Developer can use Power Automate. And the bigger issue, is its slow.
But it has the benefits of being able to use more connectors, a non-user connection, and can be delegated, leaving the app to continue while the flow runs in the background.
So the big use cases are normally long running jobs that I don't want to block the UI with.
If you do want to know more I've done a blog already about it here Power Apps - Client or Server Side?, and an example of delegating to a flow here How to - Power Apps Getting Polling Update from File Upload.
So the hot take is, move your code to the block, away from inline. Now I know I will get a lot of push back on this (rightfully too), but I would say:
- Its shades of grey, I'm not saying everything in block
- Change takes time, but when you get use to it
- Pro-Code is often the way to follow, as they have done all the learning for us



Top comments (1)
Great insights! This approach really hits the mark on performance and reducing technical debt. The 'less is more' philosophy is exactly what's needed to maintain a consistent user experience across complex apps.
Love the content !!!!