Power Apps  

Modularize Your Power Apps with User-Defined Functions & Types

Introduction

  • As Power Apps continues to evolve, so do the tools that help you build smarter, scalable, and more maintainable apps. Two standout features—User-Defined Functions (UDFs) and User-Defined Types (UDTs)—enable you to modularize logic and define structured data models directly within your app.
  • In this post, we’ll explore how to use UDFs and UDTs effectively in real-world scenarios—whether you’re a citizen developer or a pro.

What are UDFs and UDTs?

  1. UDF (User-Defined Function): Reusable logic blocks that take inputs, perform operations, and return results. Think of them like custom Power Fx functions—for validation, calculations, or business logic.
  2. UDT (User-Defined Type): Custom data structures that define consistent schemas (like Employee, Project, Invoice)—making your data more reliable and readable.

How to Enable & Define UDFs & UDTs?

Note. UDFs and UDTs are in preview as of writing this post. Depending on your current Power Apps version, these features may be fully available.

  1. Open the Settings of the Power App.
    Power App
  2. To enable the User Defined Functions: Navigate to the ‘Updates’ section, switch to the ‘Preview’ Tab, and find the ‘User-defined function’ feature and enable it.
     User Defined Functions
  3. To enable the User Defined Types: Navigate to the ‘Updates’ section, switch to the ‘Preview’ / ‘Experimental’ Tab, and find the ‘User-defined types’ feature and enable it.
    User Defined Types
  4. After enabling these features, you need to refresh the Power Apps.

To define the User Defined Functions & User Defined Types, follow the steps below to navigate to the App.Formulas.

App

Formulas

Define UDFs

UDF Syntax

FunctionName(Parameter1: ParameterType, Parameter2: ParameterType, ....): ReturnType = 
{ 
    // Your function logic here
} 

Example

IsAdult(Age: Number):Boolean = 
{
    Age >= 18
}; 

Define UDTs

UDT Syntax

TypeName := Type({
    Property1: Type1,
    Property2: Type2,
    ...
})

Example

Employee := Type( { 
    Name: Text, 
    Email: Text, 
    Department: Text, 
    StartDate: Date 
} ); 

Example Use Cases

Examples Of User-Defined Functions.

Example 1

GetApplicableDiscountRate(customerType: Text, totalAmount: Number, promoCode: Text): Number =  
{ 
	With( 
        { 
            baseDiscount: Switch( 
                customerType, 
                "VIP", 0.10, 
                "Employee", 0.15, 
                "Regular", 0.0,  
                0.0 
            ), 
            volumeDiscount: If( 
                totalAmount >= 500, 
                0.05, 
                0.0 
            ), 
            promoDiscount: Switch( 
                Upper(promoCode), 
                "SAVE10", 0.10, 
                "FREESHIP", 0.05, 
                "", 0.0, 
                0.0 
            ) 
        }, 
        Min( 
            baseDiscount + volumeDiscount + promoDiscount, 
            0.30 
        ) // Cap total discount at 30% 
    ) 
}; 

This example demonstrates a basic use of a User-Defined Function by defining input parameters with their respective types. It showcases how to structure a clean, reusable function for calculating discount logic.

Example 2

ValidateField(fieldName: Text, value: Text): Boolean =  
{ 
	Switch( 
        fieldName, 
        "Email", IsMatch(value, "^[\w\.\-]+@([\w\-]+\.)+[a-zA-Z]{2,}$"), 
        "Phone", IsMatch(value, "^\+?\d{10,15}$"), 
        "DateOfBirth", DateValue(value) <= Today(), 
        false 
    ) 
}; 

This example showcases a dynamic form builder where fields require different validation rules. It highlights how User-Defined Functions can streamline complex, field-specific validation logic in a clean and reusable way.

Examples Of User-Defined Types

Example 1

CustomerPurchaseContext := Type( { 
    CustomerType: Text, 
    TotalAmount: Number, 
    PromoCode: Text 
}); 

This example illustrates the creation of a custom User-Defined Type named CustomerPurchaseContext, providing a structured and reusable data model for consistent context handling in the App.Formulas.

Example 2

SupportTicket := Type( { 
    Subject: Text, 
    Description: Text, 
    Priority: Text, 
    SubmittedBy: Text, 
    Timestamp: DateTime 
}); 

This example demonstrates the definition of a custom User-Defined Type named SupportTicket, used to model structured support-related data within your app.

What Happens When You Combine the Power of User-Defined Functions and User-Defined Types?
Let’s explore how they work even better together

Example 1

CustomerPurchaseContext := Type(
{
    CustomerType: Text,
    TotalAmount: Number,
    PromoCode: Text
});

GetDiscount(CustomerPurchaseContext: CustomerPurchaseContext): Number =
{
    With(
        {
            baseDiscount: Switch(
                CustomerPurchaseContext.CustomerType,
                "VIP", 0.10,
                "Employee", 0.15,
                "Regular", 0.0,
                0.0
            ),
            volumeDiscount: If(
                CustomerPurchaseContext.TotalAmount >= 500,
                0.05,
                0.0
            ),
            promoDiscount: Switch(
                Upper(CustomerPurchaseContext.PromoCode),
                "SAVE10", 0.10,
                "FREESHIP", 0.05,
                "", 0.0,
                0.0
            )
        },

        // Return the final discounted total amount
        CustomerPurchaseContext.TotalAmount * (
            1 - Min(
                baseDiscount + volumeDiscount + promoDiscount,
                0.30
            )
        )
    )
};

In this example, we first define a custom type called CustomerPurchaseContext and then use it as a parameter type in the GetDiscount function.

What makes this approach unique and efficient is that instead of passing multiple individual parameters (like customer type, purchase amount, or discount eligibility), we pass a single structured object. This allows us to access all relevant properties directly within the function, improving readability, reducing repetition, and making the function easier to scale or update.

Example 2

SupportTicket := Type(
{
    Subject: Text,
    Description: Text,
    Priority: Text,
    SubmittedBy: Text,
    Timestamp: DateTime
});

BuildSupportTicket(subject: Text, desc: Text, priority: Text): SupportTicket =
{
    Subject: subject,
    Description: desc,
    Priority: priority,
    SubmittedBy: User().Email,
    Timestamp: Now()
};

In this example, we define a custom data type called SupportTicket and then build a UDF that returns data in this structured format.

What makes this approach valuable is that the function not only processes incoming parameters but also enriches the data by adding new fields and returns a well-defined SupportTicket object.

This opens the door for powerful use cases: you can call this function inside a Set() or Collect() to store or manipulate structured ticket data consistently across your app. You could also use Collect() within the function itself, depending on your needs.

By returning a UDT from a UDF, you gain stronger data consistency, easier integration, and a clean, modular way to manage complex logic.

Using UDFs in Your App

  • Once you've defined a UDF, you can call it directly from your app wherever it makes sense, just like any built-in Power Fx function.
  • For example, you can trigger the function from a button’s OnSelect property, or use it within a formula for labels, forms, or data operations—depending on your app’s requirements and logic flow.

Over to You

Have you tried UDFs or UDTs yet?

Drop your best tips, questions, or real-world examples in the comments. I’d love to hear what you’re building and how you're using them!

If you’re interested, stay connected and feel free to message me with your suggestions or requests. You can always reach out directly if you'd like to dive deeper into Power Apps together.