DAX user-defined functions: write the logic once, call it everywhere
DAX user-defined functions are in public preview in Power BI. What they are, what the syntax looks like, and how to start using them without making a mess of your model.
DAX finally has functions you can write yourself. User-defined functions (UDFs) are now in public preview in Power BI, shipped with the September 2025 release that landed alongside FabCon Vienna. You define a piece of DAX logic once, give it parameters, and call it from measures, calculated columns, visual calculations and other functions.
For anyone who maintains a semantic model with hundreds of measures, this is a real change. Copy-paste has been the default way to reuse DAX for as long as DAX has existed. Every copy is a place where a business rule can drift.
What a UDF is
A UDF is a named, parameterised DAX expression that lives in the model. It uses a new FUNCTION keyword, and once it is saved to the model you call it the same way you call a built-in function.
You can author functions in two places in Power BI Desktop:
- DAX query view, where you can define, evaluate and test a function in a query before you save it.
- TMDL view, where functions are scripted alongside the rest of the model definition.
Calculation groups already gave us some reuse, but they work by swapping in logic around an existing measure. A UDF is closer to a function in a programming language: it takes inputs and returns a result.
The syntax in practice
Here is the pattern from the Microsoft documentation, a small tax function defined and tested in DAX query view:
DEFINE
FUNCTION AddTax = (
amount : NUMERIC
) =>
amount * 1.1
EVALUATE
{ AddTax ( 10 ) }
// Returns 11
The parts are simple: a name, a list of parameters in parentheses, the => arrow, and the body. In DAX query view you then save the function to the model, either with Update model with changes or with the Update model: Add new function code lens above the definition.
Once it is in the model, you use it like any other function:
Total Sales with Tax = AddTax ( [Total Sales] )
The same function works in a calculated column. The documentation recommends wrapping the result in CONVERT there, so the column gets a consistent data type:
Sales Amount with Tax = CONVERT ( AddTax ( 'Sales'[Sales Amount] ), CURRENCY )
Functions can also call other functions, so you can build small helpers and compose them.
Parameters and evaluation
This is the part worth slowing down for. Parameters can carry optional type hints, and in the preview a function supports up to 12 parameters. The type can be a value (AnyVal, Scalar or Table) or a reference (AnyRef). Scalars can also be narrowed to a subtype such as NUMERIC, INT64 or STRING.
The more important choice is the parameter mode:
valevaluates the argument once, before the function runs, in the caller's context. This is the default.exprpasses the expression unevaluated, and the function decides when and in which context to evaluate it.
That difference matters as soon as your function uses CALCULATE or changes filter context. With val, a table argument arrives already filtered, and a filter change inside the function has no effect on it. With expr, the function can re-evaluate the expression under its own filters. If a UDF returns numbers you did not expect, check the parameter mode first.
There is also a set of type-checking functions, such as ISSTRING, ISINT64 and ISNUMERIC, that you can call inside a function body to handle different input types safely.
What this means for you
After two decades in data, the problems I see most often in semantic models are not hard calculations. They are the same calculation written five slightly different ways by five people over three years. UDFs give you a tool to stop that, but only if you use them with some discipline.
A sensible way to start:
- Pick one business rule that already exists in many measures, such as a VAT calculation, a margin definition or a fiscal period rule.
- Write it as a UDF in DAX query view and test it with
EVALUATEagainst known results. - Save it to the model and replace the copies, one measure at a time, comparing the numbers before and after.
- Agree on a naming convention for functions before the second person starts writing them.
- Keep it in a test model or a development copy until you are comfortable. This is a preview, and I would not build production standards around it yet.
Two things to keep in mind from the documented limitations. Recursion is not supported, so a function cannot call itself. Function overloading is not supported either, so one name means one signature.
Also think about who will maintain the model after you. A model full of nested functions with clever expr parameters can be harder to read than plain measures. Use UDFs for rules that genuinely repeat, not for every expression you write.
Takeaway
UDFs are the most useful change to day-to-day DAX authoring in a long time, because they target the problem most models actually have: duplicated logic. Start small, test against numbers you trust, and decide on conventions early.
Which business rule in your model would you turn into a function first?
Sources
Enjoyed this? Get the next one by email
Occasional emails about Microsoft Fabric, SQL Server, Power BI and Synapse.