Learn exactly when to reach for Set(), UpdateContext(), and Collect() in Power Apps Canvas Apps. This practical lesson walks you through realistic scenarios for each variable type and gives you a clear decision framework to avoid the most common state-management mistakes.

Imagine you're building a shift-scheduling app for a logistics company. A manager selects their department from a dropdown on the home screen, navigates to a weekly schedule view, selects a specific day, and then drills into an individual employee's record to edit their hours. At every step, the app needs to remember things: which department was selected, which day is being viewed, which employee is being edited. None of that information lives in a database. It's temporary, in-memory state that only needs to exist while the app is running.
This is the problem that variables solve in Power Apps — and solving it badly is one of the most common reasons Canvas Apps become slow, buggy, and nearly impossible to maintain. Understanding which type of variable to reach for, and why, is one of those foundational skills that separates apps that merely work from apps that work well.
By the end of this lesson, you'll have a clear mental model of the three variable mechanisms Power Apps offers, know exactly when to use each one, and be able to read and write variable-related formulas with confidence.
What you'll learn:
You should be comfortable opening Power Apps Studio and have built at least a basic app with a couple of screens and controls. If you haven't done that yet, work through Build Your First Canvas App in Power Apps first. You don't need any programming experience — we'll explain every concept from scratch.
In traditional programming, a variable is a named storage location in memory that holds a value. Power Apps works on the same basic principle, but with a twist that surprises a lot of newcomers: Canvas Apps are formula-driven, not code-driven.
In Excel, you don't declare a variable to hold a cell's value — you just reference the cell. Power Apps borrows this philosophy. Most of the time, you bind a control's property directly to a data source or formula, and the result recalculates automatically when inputs change. Variables are for situations where that automatic recalculation isn't enough — when you need to store a value that results from a user action and carry it forward to a later point in the app's lifecycle.
Think of variables as sticky notes. When a user does something meaningful — selects a record, toggles a filter, completes a step in a wizard — you write that information on a sticky note. Later, when another screen or control needs to know about it, it reads the note. Variables are those sticky notes.
Power Apps gives you three kinds of sticky notes, each designed for a different situation:
| Type | Function | Stores | Scope |
|---|---|---|---|
| Global Variable | Set() |
A single value | Entire app |
| Context Variable | UpdateContext() |
A single value (or record) | One screen only |
| Collection | Collect() / ClearCollect() |
A table of records | Entire app |
Let's take each one seriously.
A global variable is the simplest variable type. It holds a single value — a number, a text string, a boolean (true/false), a date, or even a record — and that value is accessible from any screen in the app.
You create and update a global variable using the Set() function. The syntax is straightforward:
Set(VariableName, Value)
If the variable doesn't exist yet, Set() creates it. If it already exists, Set() replaces its value. There's no separate "declare" step.
Let's say your logistics scheduling app needs to show different options to managers versus regular employees. When the app starts, you want to detect the user's role and store it so every screen can reference it.
In the OnStart property of the App object (click on the App in the left tree view, then find OnStart in the property dropdown), you'd write:
Set(
gblCurrentUserRole,
If(
User().Email = "operations-manager@contoso.com",
"Manager",
"Employee"
)
)
Now, anywhere in the app, you can reference gblCurrentUserRole in formulas. A button's Visible property might read:
gblCurrentUserRole = "Manager"
And that button will only appear for managers.
Tip: Prefix global variable names with
gbl(e.g.,gblCurrentUserRole,gblSelectedDepartment). It's a convention many Power Apps developers use to instantly distinguish variable references from control names and function calls at a glance. Context variables often useloc(for "local") for the same reason.
Global variables shine when a piece of information needs to be read across multiple screens. Common use cases include:
Here's the trap beginners fall into: using global variables for everything because they seem simpler. The problem is that global variables have no automatic relationship to the screen they were set on. If you use a global variable to track which record a gallery is displaying, but the user navigates back and the gallery selection changes, you might end up with stale data. Context variables, which we'll look at next, are a better fit for screen-specific state.
Warning: Don't use global variables to pass a value between a parent screen and a sub-screen when that value is only relevant to those two screens. You'll end up polluting the app's global namespace with variables that are only meaningful in one tiny context — a maintenance headache waiting to happen.
Context variables work almost identically to global variables, with one crucial difference: they only exist within the screen where they were created. A context variable set on Screen A is invisible on Screen B. This scoping is a feature, not a limitation — it keeps each screen's internal state clean and isolated.
You create and update context variables with UpdateContext():
UpdateContext({VariableName: Value})
Notice the curly braces. UpdateContext() takes a record as its argument, which means you can set multiple context variables in one call:
UpdateContext({locIsLoading: true, locErrorMessage: ""})
This is genuinely useful and more efficient than calling Set() twice.
One of the most common uses for context variables is controlling whether a confirmation dialog is visible. Say your scheduling app has a "Delete Shift" button. You want clicking it to reveal a confirmation panel (not a pop-up — Power Apps doesn't have native pop-ups, so developers simulate them with overlapping containers that are shown or hidden).
The "Delete Shift" button's OnSelect property:
UpdateContext({locShowDeleteConfirmation: true})
The confirmation panel's Visible property:
locShowDeleteConfirmation
The "No, Cancel" button inside the confirmation panel:
UpdateContext({locShowDeleteConfirmation: false})
The "Yes, Delete" button:
Patch(ShiftSchedules, GalleryShifts.Selected, {IsDeleted: true});
UpdateContext({locShowDeleteConfirmation: false})
This entire dialog pattern — open, confirm, close — lives entirely within one screen. Using a global variable for locShowDeleteConfirmation would be wrong; the confirmation state is purely a local concern of that screen.
Here's something that trips people up: because context variables are screen-scoped, you cannot read a context variable from another screen. However, you can pass a value into a screen when you navigate to it, using the second argument of the Navigate() function:
Navigate(
EmployeeDetailScreen,
ScreenTransition.Fade,
{locSelectedEmployeeID: GalleryEmployees.Selected.EmployeeID}
)
This creates a context variable called locSelectedEmployeeID on EmployeeDetailScreen at the moment of navigation. It's like handing someone a sticky note as they walk into a room, rather than leaving it on a global bulletin board.
Key insight: Use
Navigate()with a context record to pass just the information a screen needs to do its job. This keeps screens loosely coupled —EmployeeDetailScreengets the ID it needs, but it doesn't need to know anything about what happened on the previous screen.
If you're building multi-screen apps, this pattern comes up constantly. You can learn more about the interplay between navigation and state in Building Multi-Screen Apps with Navigation and Variables in Power Apps.
locIsLoading: true while data fetchesNavigate()A collection is fundamentally different from both types of variables above. While Set() and UpdateContext() store a single value (even if that value is a record), a collection stores a full table — multiple rows and multiple columns, just like a spreadsheet or a database table, held entirely in the device's memory.
Collections are global in scope (like global variables), meaning they're accessible from any screen. They're created and managed with several functions, but the two you'll use most are:
Collect(CollectionName, NewRecord)
Collect() adds a record to the collection without removing what's already there.
ClearCollect(CollectionName, DataSource)
ClearCollect() clears the collection first, then fills it with the data you provide. Use this when you want to take a snapshot of a data source and work with it locally.
Imagine your scheduling app lets managers build a schedule by "adding" employee shifts to a draft before submitting them all at once. While the manager is assembling the schedule, no data should be written to SharePoint — it's all just in-flight drafts.
When the manager clicks "Add to Schedule":
Collect(
colDraftSchedule,
{
EmployeeID: locSelectedEmployeeID,
EmployeeName: locSelectedEmployeeName,
ShiftDate: DatePicker1.SelectedDate,
ShiftStart: DropdownStart.Selected.Value,
ShiftEnd: DropdownEnd.Selected.Value
}
)
Each click adds another row to colDraftSchedule. A gallery on the same screen can display colDraftSchedule as its data source, giving the manager a live preview of the draft schedule. When they're satisfied and hit "Submit All":
ForAll(
colDraftSchedule,
Patch(ShiftSchedules, Defaults(ShiftSchedules), ThisRecord)
);
ClearCollect(colDraftSchedule, [])
This writes every draft record to SharePoint in one batch, then clears the collection.
Tip: You can use
Remove(CollectionName, Record)to delete a specific row from a collection without clearing the whole thing. This is how you build the "remove item from cart" interaction: the gallery's delete icon setsRemove(colDraftSchedule, ThisItem)in itsOnSelectproperty.
Collections are also useful for caching a data source locally to reduce repeated network calls. Instead of querying SharePoint every time a screen loads, you can load the data once into a collection and reference the collection throughout:
// In App.OnStart
ClearCollect(
colAllEmployees,
Filter(Employees, IsActive = true)
)
Now colAllEmployees is available instantly on every screen without hitting the network again. This matters for performance. Be aware, though, that ClearCollect() loads all returned rows into memory — if your data source has thousands of rows, this can be slow and memory-intensive. For deeper guidance on how data row limits affect this pattern, see Power Apps Performance Optimization: Delegation, Data Row Limits, and Reducing App Load Times.
Warning: Data cached in a collection won't automatically refresh if the underlying data source changes while the user has the app open. If real-time accuracy matters, either re-run
ClearCollect()at appropriate moments (like when returning to a screen) or don't cache at all.
For a much deeper dive into everything collections can do — including Patch, ForAll, and complex in-memory operations — check out Power Apps Collections and Local Data Management: Mastering ClearCollect, Patch, and In-Memory Data Operations.
When you're staring at a blank formula bar and wondering which variable type to reach for, work through these questions:
1. Does this value need to be a table (multiple rows and columns)? Yes → Use a Collection. No → Continue.
2. Does this value need to be visible on more than one screen?
Yes → Use a Global Variable (Set()).
No → Continue.
3. Is this value only relevant to the current screen's internal behavior?
Yes → Use a Context Variable (UpdateContext()).
Still unsure → Default to a Context Variable. You can always promote it later.
Here's a practical cheat sheet:
| Scenario | Best Choice |
|---|---|
| Store the logged-in user's name at startup | Global Variable |
| Track whether a delete confirmation panel is open | Context Variable |
| Cache a list of product categories from SharePoint | Collection |
| Pass a selected record's ID to a detail screen | Context Variable (via Navigate) |
| Build a draft order before submitting | Collection |
| Toggle dark mode across the whole app | Global Variable |
| Track which wizard step the user is on (one screen) | Context Variable |
| Store items a user has multi-selected in a gallery | Collection |
Let's put all three types to work. You'll build a small three-screen app that demonstrates each variable type in a realistic scenario.
Setup: Open Power Apps Studio and create a new blank canvas app for tablet layout.
Screen 1 — Home Screen
Add a Text Label control. Set its Text property to "Welcome, " & gblUserName. It will show empty for now.
Add a Button labeled "Enter App." Set its OnSelect to:
Set(gblUserName, User().FullName);
Navigate(DepartmentScreen, ScreenTransition.Fade)
Screen 2 — Department Screen
Add a Dropdown control named DropdownDept. Set its Items property to:
["Operations", "Logistics", "Warehouse", "Admin"]
Add a Button labeled "View Team." Set its OnSelect to:
ClearCollect(
colTeamMembers,
Filter(
Table(
{Name: "Alice Chen", Department: "Operations"},
{Name: "Marcus Webb", Department: "Logistics"},
{Name: "Priya Nair", Department: "Operations"},
{Name: "James Okafor", Department: "Warehouse"}
),
Department = DropdownDept.Selected.Value
)
);
Navigate(
TeamScreen,
ScreenTransition.Fade,
{locSelectedDepartment: DropdownDept.Selected.Value}
)
This creates a hardcoded table (replace with your actual data source in a real app), filters it, stores the result in a collection, and passes the department name as a context variable to the next screen.
Screen 3 — Team Screen
Add a Text Label. Set Text to "Team: " & locSelectedDepartment.
Add a Gallery. Set Items to colTeamMembers. Add a label inside showing ThisItem.Name.
Add a Button labeled "Back." Set OnSelect to Navigate(DepartmentScreen, ScreenTransition.None).
Test the app by pressing F5 (or the Play button). Observe:
gblUserName carries your name from Screen 1 all the way to — potentially — every screenlocSelectedDepartment exists only on Team Screen, passed in via Navigate()colTeamMembers is a filtered, in-memory table that the gallery reads without touching a real data sourceTry changing the dropdown selection on Screen 2, clicking "View Team" again, and watching the gallery update.
"My context variable is empty on the new screen."
You navigated to the screen but didn't pass the context in the third argument of Navigate(). Go back to your button's OnSelect and add the context record as the third argument.
"My global variable still has last session's value."
Global variables don't persist between sessions — they reset every time the app loads. If you need persistence, store the value in a data source (SharePoint, Dataverse) and reload it in App.OnStart.
"My collection has duplicate rows every time I add an item."
You're using Collect() when you should be using ClearCollect() — or you have a Collect() in a location that runs multiple times (like a screen's OnVisible, which fires every time the screen is navigated to). Either use ClearCollect(), or check whether the record already exists before collecting.
"Referencing my variable gives me an error about it not existing."
Variables are created the first time Set(), UpdateContext(), or Collect() runs. If the formula that reads the variable runs before the formula that sets it, the variable will be blank or throw an error. Move initialization to App.OnStart for global variables and collections.
Note: Power Apps variables don't have data types in the traditional sense — they infer type from the first value you assign. If you
Set(gblCount, 0)and later doSet(gblCount, "five"), you won't get an error, but formulas that treatgblCountas a number will behave unexpectedly. Be consistent with the types you store in each variable.
"My app feels slow when I use ClearCollect on OnVisible."
OnVisible fires every time you navigate to a screen. If ClearCollect() is there with a large query, your users will experience a delay every single navigation. Move one-time loads to App.OnStart and only refresh in OnVisible if truly necessary.
For a deeper look at diagnosing variable-related performance and behavior issues, the Debugging Canvas Apps: Using the Power Apps Monitor Tool and Formula Errors to Fix Issues Fast lesson walks you through reading the Monitor tool to trace exactly when and where variable assignments are firing.
Let's bring it all together. Canvas Apps give you three variable mechanisms, each with a distinct role:
Set()) — single values, app-wide scope, set once and read anywhere. Best for user identity, app-wide toggles, and cross-screen state.UpdateContext()) — single values, screen-scoped, ideal for UI state like dialog visibility, loading indicators, and values passed in via Navigate().Collect() / ClearCollect()) — in-memory tables, app-wide scope, perfect for draft data, multi-selection, and caching reference data locally.The discipline of choosing the right type pays dividends as your apps grow. Global variables used carelessly turn into a tangled web of state that's hard to debug. Context variables that should be global create navigation bugs. Collections loaded recklessly bring performance to its knees.
As a next step, practice building the three-screen exercise above with a real data source — connect your app to SharePoint or Excel using the guidance in Connecting Power Apps to SharePoint, Excel, and Dataverse: A Complete Integration Guide, then swap the hardcoded Table() in the exercise for a real Filter() against a connected list.
When you're ready to go deeper into formula-driven logic that works alongside your variables — including Patch, Filter, Lookup, and Navigate — Master Power Apps Formulas: Navigate, Filter, Lookup, and Patch for Professional Apps is the natural next lesson in this path.