Mastering Patch in Power Apps: Create, Update, and Upsert Records
Learn how to use the Patch function for single and batch operations, with a real-world project tasks example.
Power Apps can read and write data through a few functions: Patch is the most flexible — and the most misunderstood. The same function handles creating, updating, merging, and even changing in‑memory variables, which is why newcomers find it confusing.
Rather than memorising seven separate syntaxes, think of Patch as a single pattern: you provide a data source, one or more records, and then a set of changes to apply. The exact shape of those arguments determines whether you are creating, updating, or doing both at the same time.
In this article you'll build a practical example using a SharePoint list called ProjectTasks. Every snippet is production‑ready, and I've added performance notes where delegation matters.
Scenario: ProjectTasks list
Our list tracks tasks with these columns:
| Internal Name | Type |
|---|---|
TaskID | Number (auto‑increment) |
TaskName | Text |
Owner | Person or Group |
Status | Choice (Not Started, In Progress, Completed) |
DueDate | Date only |
Priority | Choice (Low, Medium, High) |
All examples assume the app has a data source named ProjectTasks connected to this SharePoint list.
1. Create a single task
The simplest Patch call creates a new record. Use Defaults(<data source>) as the base record, then supply a record of column values to set.
Patch(
ProjectTasks,
Defaults(ProjectTasks),
{
TaskName: "Design landing page",
Owner: {Claims: "i:0#.f|membership|emily@contoso.com", DisplayName: "Emily Tran"},
Status: "Not Started",
DueDate: Date(2026, 6, 30),
Priority: "High"
}
)
This returns the newly created record, including system fields like TaskID and Created. You can capture it if you need the ID later.
2. Update an existing record
To change one or more fields, pass the existing record (obtained with LookUp or First) as the second argument and the changes as the third argument. Only the columns you include are updated; everything else stays the same.
Patch(
ProjectTasks,
LookUp(ProjectTasks, TaskID = 4),
{
Status: "Completed",
Priority: "Medium"
}
)
Delegation note: LookUp is delegable when the predicate is a simple equality on the primary key. If you cannot guarantee delegation, use Filter with a top‑n limit or consider a gallery selection to obtain the record.
3. Capture the result in a variable
Patch returns the modified record, which is especially useful for getting the new ID or the full record after creation.
Set(
varCurrentTask,
Patch(
ProjectTasks,
Defaults(ProjectTasks),
{
TaskName: "Fix login bug",
Owner: {Claims: "i:0#.f|membership|jay@contoso.com", DisplayName: "Jay R. "},
Status: "In Progress",
DueDate: Date(2026, 7, 5),
Priority: "High"
}
)
)
After this runs, varCurrentTask contains the entire record – including the TaskID assigned by SharePoint. You can use it immediately in a notification or navigate to a detail screen.
4. Create multiple records in one call
Batch creation reduces network round‑trips. Build a table of new records, then use ForAll to generate a matching number of Defaults records.
ClearCollect(
colNewTasks,
Table(
{TaskName: "Write tests", Owner: ..., Status: "Not Started", DueDate: Date(2026,7,1), Priority: "Medium"},
{TaskName: "Review PR #42", Owner: ..., Status: "Not Started", DueDate: Date(2026,7,2), Priority: "Low"},
{TaskName: "Update docs", Owner: ..., Status: "Not Started", DueDate: Date(2026,7,3), Priority: "High"}
)
);
Patch(
ProjectTasks,
ForAll(Sequence(CountRows(colNewTasks)), Defaults(ProjectTasks)),
colNewTasks
)
Performance note: When Patch receives table arguments, SharePoint and SQL Server support sending multiple changes in fewer network requests. For Dataverse, each row is a separate Web API call regardless of the table-form syntax, so consider using a Power Automate flow with bulk operations for very large sets.
5. Edit multiple existing records
To update several records at once, pass a table of records that includes the primary key (TaskID) and only the columns to change. The trick is to supply a base‑record table that contains just the IDs.
ClearCollect(
colUpdates,
Table(
{TaskID: 2, Status: "Completed"},
{TaskID: 5, Priority: "High"},
{TaskID: 9, DueDate: Date(2026, 7, 15)}
)
);
Patch(
ProjectTasks,
ShowColumns(colUpdates, "TaskID"),
colUpdates
)
Each record in the third table is matched to the corresponding base record by position, not by ID value. This is the fastest way to apply different changes to different rows in one call.
6. Upsert: create or update by ID
Upsert is a single call that creates records where the ID is blank and updates records where the ID matches an existing row. The second argument becomes the combined table, and the third argument is omitted.
ClearCollect(
colUpsert,
Table(
{TaskID: 3, Status: "In Progress", Owner: ...}, // updates task 3
{TaskID: Blank(), TaskName: "New task", Owner: ..., Status: "Not Started", DueDate: Date(2026,8,1), Priority: "Low"} // creates new
)
);
Patch(
ProjectTasks,
colUpsert
)
The first record contains a non‑blank TaskID, so it updates. The second has Blank() for TaskID, so a new record is created. This pattern is ideal for synchronising data from an external source.
7. Update an in‑memory variable without touching the data source
Patch is not limited to data sources. When you pass two records of the same type, it merges the second into the first and returns a new copy. The original variable is not mutated – you must assign the result.
// Assume varTask is a record variable
Set(
varTask,
Patch(
varTask,
{Priority: "High", Status: "In Progress"}
)
)
This is a clean way to modify a record variable without writing back to the data source. Useful for staging changes before a user confirms.
Remember: Patch with two records returns a new record; it does not alter the original variable. Always capture the result with Set or a context variable update.
Common mistakes and how to avoid them
- Forgetting to capture the result – If you need updated fields or the new ID, always assign the return value to a variable.
- Mixing Person column formats – The
Ownercolumn requires a special structure. Use{Claims: "i:0#.f|membership|alias@domain.com", DisplayName: "Name"}. - Passing a table where a record is expected – The third argument of
Patchis a record, not a table. For multiple changes, use the batch pattern shown above. - Over‑fetching in LookUp – When updating a single record, prefer
LookUpoverFilterbecause it returns one record and is often delegable.
Delegation and performance
Patchoperations are not delegated to the data source in the traditional sense; the entire operation happens in the server but is limited by the data source's API.- For batch operations, SharePoint and SQL Server support sending multiple changes in a single request. Dataverse treats each row as a separate web API call, so batch create/update may not be faster than a loop.
- Always filter the base table as much as possible before calling
Patchto minimise the data sent over the network.
Final recommendation
Start with the single‑record create and update patterns – they cover 80 % of real‑world forms. Once you’re comfortable, explore batch operations to handle scenarios like importing spreadsheets or bulk status changes. The upsert pattern is a powerful tool for integrations, and the two‑record variable form can simplify complex screen logic.
Nothing beats experimenting in a small test app. Create a SharePoint list with a few rows and try each of the snippets above – you’ll quickly build an intuition for when Patch is the right tool.
References
- Original cheat sheet by Matthew Devaney: 7 Ways To Use The PATCH Function In Power Apps (Cheat Sheet)
- Microsoft Learn: Patch function in Power Apps
- Microsoft Learn: Defaults function
- Microsoft Learn: Create or update bulk records in Power Apps