Constructing Clean Canvas Apps: A Guide to Power Apps Coding Practices
A practical walkthrough of naming conventions, error handling patterns, and performance strategies to keep your canvas apps scalable and maintainable.
Building a Power Apps canvas app that works is one step; building one that can be understood, extended, and debugged by others (or by yourself six months later) is the real challenge. Without a consistent set of coding conventions, even a moderately complex app quickly becomes a maze of disconnected variables, long formulas, and slow galleries. This article shares a practical framework for writing clean, maintainable canvas apps using a concrete scenario you can adapt to your own projects.
The Scenario: Project Task Tracker
Imagine you are creating a task management app for a small team. Your data lives in a SharePoint list called ProjectTasks with these columns:
| Column | Type |
|---|---|
Title | Single line of text |
AssignedTo | Person or group |
Status | Choice (Not Started, In Progress, Completed) |
DueDate | Date and Time |
You need a gallery that displays tasks assigned to the current user and a detail screen where those tasks can be edited. All the examples that follow are based on this scenario.
1. Naming Conventions as the Blueprint
Consistent naming makes an app self-documenting. The few seconds you save by calling a control Label1 are lost many times over when you try to fix a bug.
Controls
Use a short prefix that indicates the control type:
lblPageTitle // Label txtNewTask // Text input btnSubmit // Button galTasks // Gallery scrDetail // Screen cmpHeader // Component colStatus // Collection (always hint at data shape)
Variables, Collections, and Data Sources
- Global variables:
gblCurrentUser - Context variables:
varSelectedTaskId(use in Screen.OnVisible or Navigate context) - Collections:
colLocalTasks - Data sources: use the original SharePoint/Dataverse name, e.g.,
ProjectTasks
Avoid single-letter variables. Prefer camelCase for variables and PascalCase for data source names.
Screens
Name screens by their function: scrTaskBrowse, scrTaskDetail, scrSettings. Avoid generic names like Screen1.
2. Screen Architecture and Navigation
For the task tracker, use at least two screens:
- Browse screen (scrTaskBrowse) – a gallery that filters tasks for the current user.
- Detail screen (scrTaskDetail) – a form to view and edit a selected task.
Pass the selected record via navigation context rather than a global variable:
Navigate(scrTaskDetail, ScreenTransition.Fade, {selectedTask: ThisItem})On the detail screen, capture the context into a local variable:
Set(varSelectedTask, selectedTask)
This keeps the data flow clear and avoids accidental overwrites from other screens.
3. Handling Data with Delegation in Mind
Delegation is the single most common performance trap in canvas apps. The ProjectTasks list can hold thousands of records, so your gallery filter must delegate.
Good (delegable):
Filter(ProjectTasks, AssignedTo.Email = gblCurrentUser)
Bad (non‑delegable):
Filter(ProjectTasks, StartsWith(Title, "Design") && Status.Value = "Not Started")
StartsWith on a text column is delegable, but && is not when combined with another filter? Actually, StartsWith is delegable; combining with a delegable condition is fine. However, Status.Value (a choice column) might not be fully delegable depending on the data source. A safer approach is to use the underlying ID or use choices directly.
For choices, filter by the value like Status = 'Not Started' (using the single-quoted status value). SharePoint choices are delegable when you use the exact value.
If a delegation warning remains, consider restructuring your data (e.g., add a calculated column) or using With to reduce the dataset locally after a delegable first step.
Use With to Cache Repeated Values
When you need to reference the same record multiple times, use With to avoid repeated lookups:
With(
{currentTask: LookUp(ProjectTasks, ID = varSelectedTaskId)},
If(
currentTask.Status.Value = "Completed",
"Done",
currentTask.Title
)
)This is especially valuable inside galleries or labels where a formula is evaluated many times.
4. Error Handling Pattern
With modern IfError and IsEmpty you can centralise error handling instead of checking Result after every Patch.
Validation before saving:
If(
IsBlank(txtNewTask.Text),
Notify("Title is required.", NotificationType.Error),
IsBlank(DatePickerDueDate.SelectedDate),
Notify("Due date must be set.", NotificationType.Error),
Patch(ProjectTasks, Defaults(ProjectTasks), {
Title: txtNewTask.Text,
AssignedTo: gblCurrentUser,
Status: 'Not Started',
DueDate: DatePickerDueDate.SelectedDate
});
Notify("Task created.", NotificationType.Success);
Reset(txtNewTask);
Reset(DatePickerDueDate)
)Catching errors with IfError:
IfError(
Patch(ProjectTasks, varSelectedTask, {Status: 'Completed'}),
Notify("Update succeeded.", NotificationType.Success),
Notify("Update failed: " & FirstError.Message, NotificationType.Error)
)Use FirstError.Message to surface the description rather than a cryptic error code.
5. Performance Quick Wins
- Trim OnStart – load only essential data. Move secondary lookups to the first screen’s
OnVisible.
Set(gblCurrentUser, User().Email); Collect(colStatusOptions, Choices(ProjectTasks.Status));
-
Avoid unnecessary dependencies – each control that depends on another control increases formula re-evaluation. Store derived values in variables when possible.
-
Use collections for static data – if a lookup table rarely changes, load it once into a collection and use
Filteron that collection locally. -
Reduce control count – reuse components (header, footer, icons) instead of repeating them on every screen.
-
Watch the delegation bar – whenever you add a filter, check the yellow delegation warning in the upper-left corner of the formula bar. If it appears, you must rewrite the filter or the app will break with scaled datasets.
6. Common Pitfalls and How to Avoid Them
| Pitfall | Consequence | Solution |
|---|---|---|
| Inconsistent naming | Hard to find variables and controls | Agree on prefixes and casing early; use Power Apps’ “View > Variables” and “View > Controls” panes to audit. |
| Ignoring delegation | App works in test but fails in production | Each time you write a Filter or LookUp, check the delegation warning. |
| Long formulas in single lines | Impossible to read | Use With to break logic into steps; use // comments inside formulas. |
| Hard‑coded data source names | Updating a source name requires changing every formula | Use a global variable for the data source name and reference that. Better yet, use environment variables. |
| Skipping error handling | User sees a generic dialog on failure | Wrap every data mutation in IfError or check IsEmpty after a Patch. |
Final Recommendation
Start with a small set of standards that your team agrees on. Write them down in a shared location (a wiki or a simple markup file) and review them during pull requests or code walkthroughs. The goal is not to enforce an arbitrary style but to reduce cognitive overhead so everyone can focus on the logic that matters.
The official Power Apps canvas app coding standards provide a solid base; this article extends them with practical patterns for day‑to‑day development. Over time, evolve your own rules as the platform adds new features.
References
- Original source reference: 2022 Power Apps Coding Standards For Canvas Apps by Matthew Devaney (kept for attribution)
- Microsoft Learn: Coding Standards for Canvas Apps
- Microsoft Learn: Formula reference for Power Apps
- Microsoft Learn: Delegation in Power Apps