What it does
User-defined functions (UDFs) let you write your own Power Fx functions with typed parameters and a typed return value, defined in App.Formulas alongside named formulas. User-defined types (UDTs) let you name a record or table shape with the Type function so those functions can take and return structured data.
Key facts
- Both are generally available. UDFs reached GA in September 2025 with Power Apps Studio 2508.3, and UDTs reached GA in May 2026 with Studio version 3.26044.
- UDTs are on by default for new apps. For existing apps you enable them under Settings > Updates > New > User-Defined types.
- UDFs depend on the new analysis engine, which is why the two switches were combined at GA. It's on by default for new apps.
- Syntax is
FunctionName( Param: Type ) : ReturnType = Formula;. Every parameter and the return value must be typed — there's no inference here, unlike named formulas. - Types are defined with
:=rather than=, for exampleBook := Type( { Title: Text, Author: Text } ). RecordOf extracts the record type from a table type. - Behaviour UDFs are supported by wrapping the body in curly braces and chaining statements. Use Void as the return type when the function doesn't return a value.
- Record matching for parameters is stricter than elsewhere in Power Fx. The fields of a value must be a proper subset of the type and can't include extras — passing a record with a spare column is an error.
- Recursion isn't supported.
- UDTs work with ParseJSON, IsType and AsType, which is where they earn their keep — a typed ParseJSON validates and converts ISO 8601 date strings instead of handing you text.
When to use / skip
Use UDFs the moment the same logic appears in three control properties. That's the point where duplication starts drifting out of sync, and a UDF is easier to reason about and test in isolation than the same expression scattered across a screen. Use UDTs when you're passing records or tables around, and always when you're parsing JSON from a connector — untyped JSON handling in Power Fx was miserable before this and there's no reason to keep doing it. Skip both for a two-screen app that one person maintains; the ceremony isn't worth it.
Configuration decisions
- Whether existing apps get the UDT switch turned on, and whether that happens per app or as an estate-wide standard.
- Where the boundary sits between a named formula, a UDF, and a component with enhanced properties, since all three now do reuse and they overlap.
- Which types are defined centrally and treated as the app's data contract, versus defined ad hoc next to the function that uses them.
- How you handle logic that wants recursion, given the language doesn't offer it.
Gotchas
- The strict record matching bites when you pass something straight out of a gallery selection or a form. Extra columns that Power Fx tolerates everywhere else are rejected here, and the error doesn't obviously point at the extra field.
- Behaviour UDFs don't stop on error by themselves. Execution continues past an Error call in a chained body unless you gate it with If or IfError.
- Everything lives in App.Formulas, so functions, types and named formulas all share one unstructured text property. Large apps end up with a very long single property and no navigation.
- Existing apps don't get UDTs automatically. If a maker copies a formula from a newer app into an older one and it fails, the missing switch is the first thing to check.
Consultant notes
- These, plus enhanced component properties, are what make it defensible to build a genuinely large canvas app. If a client asks why their previous canvas project became unmaintainable, this is a large part of the honest answer.
- Both are GA now, so there's no preview caveat to attach to a delivery plan. That's a change from a year ago and worth saying out loud to clients who were told to avoid them.
- Push UDTs hard on any integration work. Typed ParseJSON removes a category of runtime failures that used to surface as blank labels with no error.
- Set the convention early that shared logic lives in App.Formulas and not in a component nobody remembers exists. The tooling doesn't enforce a choice, so you have to.
Worth revisiting when recursion or module-style organisation for App.Formulas ships.