From 40a842f0dfd14e9f05b7170ed0f817d5520656a0 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 11 May 2026 18:33:48 -0400
Subject: [PATCH 01/11] [11.0 P4] Blazor What's New feature coverage
---
.../blazor/components/virtualization.md | 28 +++++++++++
aspnetcore/blazor/debug.md | 22 ++++++++-
.../standalone-with-identity/index.md | 23 +++++++++
aspnetcore/blazor/state-management/server.md | 48 +++++++++++++++++--
aspnetcore/blazor/tooling.md | 44 ++++++++++++++++-
.../aspnetcore-11/includes/blazor.md | 46 ++++++++++++++++--
6 files changed, 200 insertions(+), 11 deletions(-)
diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md
index e84db8887908..f8024e273485 100644
--- a/aspnetcore/blazor/components/virtualization.md
+++ b/aspnetcore/blazor/components/virtualization.md
@@ -274,6 +274,34 @@ The com
:::moniker-end
+:::moniker range=">= aspnetcore-11.0"
+
+## Control viewport scroll position behavior when items are dynamically added
+
+
+
+Assign a `VirtualizeAnchorMode` value to the `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added:
+
+* `None`: No edge pinning. The viewport stays at current scroll position regardless of item changes.
+* `Beginning`: Pins the viewport to the beginning of the list. When the user is at a scroll position near the top of the list and new items arrive at the beginning, the viewport stays at the top showing the newest items. For example, this pinning behavior is useful for a news feed user experience.
+* `End`: Pins the viewport to the end of the list. When the user is at a scroll position near the bottom of the list and new items arrive at the end, the viewport auto-scrolls to show them. If the user has scrolled away, auto-scroll disengages until they return to the bottom. For example, this pinning behavior is useful for a chat or logging user experience.
+
+The following example pins the viewport to the beginning of a virtualized flight list:
+
+```razor
+
+
+
+
+
+```
+
+Modes can be combined. For example, assigning `Beginning | End` pins both edges. Combining `None` with other modes is supported but doesn't change the combined value.
+
+`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section). The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer{T}.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically.
+
+:::moniker-end
+
## State changes
When making changes to items rendered by the component, call to enqueue re-evaluation and rerendering of the component. For more information, see .
diff --git a/aspnetcore/blazor/debug.md b/aspnetcore/blazor/debug.md
index ebcca7914baa..95552664e401 100644
--- a/aspnetcore/blazor/debug.md
+++ b/aspnetcore/blazor/debug.md
@@ -164,7 +164,27 @@ Blazor Server: [`Microsoft.AspNetCore.Components.WebAssembly.Server`](https://ww
:::moniker-end
-Standalone Blazor WebAssembly: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor apps. Calls internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools.
+
+
+Standalone Blazor WebAssembly: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor WebAssembly apps. Calls internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools.
+
+
:::moniker range="< aspnetcore-8.0"
diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md b/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md
index 4ed1294a446a..89ec24c0df0e 100644
--- a/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md
+++ b/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md
@@ -181,11 +181,34 @@ A standalone Blazor WebAssembly frontend app demonstrates user authentication an
The app uses the following NuGet packages:
+
+
* [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication)
* [`Microsoft.Extensions.Http`](https://www.nuget.org/packages/Microsoft.Extensions.Http)
* [`Microsoft.AspNetCore.Components.WebAssembly`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly)
* [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer)
+
+
### Sample app code
The `Models` folder contains the app's models:
diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md
index f4f3eddceba1..dab844bca532 100644
--- a/aspnetcore/blazor/state-management/server.md
+++ b/aspnetcore/blazor/state-management/server.md
@@ -213,7 +213,7 @@ To persist temporary data between HTTP requests during static server-side render
`TempData`:
* Is available when is called in the app's `Program` file.
-* Is provided as a cascading value with the [`[CascadingParameter]` attribute](xref:blazor/components/cascading-values-and-parameters#cascadingparameter-attribute).
+* Is provided as a cascading value with the [`[CascadingParameter]` attribute](xref:blazor/components/cascading-values-and-parameters#cascadingparameter-attribute) or the `[SupplyParameterFromTempData]` parameter attribute.
* Is accessed by key (string).
* Supports primitives, , , enums, and collections (arrays, , ).
* Stores `object?` values, requiring runtime casting (example: `var message = TempData["Message"] as string`). IntelliSense and type checking aren't supported.
@@ -224,6 +224,16 @@ To persist temporary data between HTTP requests during static server-side render
public ITempData? TempData { get; set; }
```
+When supplied to a parameter for simple read/write of a single value, use the `[SupplyParameterFromTempData]` attribute without or with a key (string):
+
+```csharp
+[SupplyParameterFromTempData]
+public string? Message { get; set; }
+
+[SupplyParameterFromTempData(Name = "flash_message")]
+public string? FlashMessage { get; set; }
+```
+
The `ITempData` interface provides the following methods for controlling value lifecycle:
* `Get`: Gets the value associated with the specified key and schedules the data for deletion.
@@ -286,15 +296,16 @@ Browsers enforce a 4 KB cookie size limit. `TempData` automatically uses @message
-
@@ -310,7 +321,7 @@ In the following example, a form displays a message that's retained in `TempData
message = TempData?.Get("Message") as string ?? "No message";
}
- private void HandleSubmit()
+ private void Submit()
{
TempData!["Message"] = "Form submitted successfully!";
NavigationManager.NavigateTo("/tempdata-example", forceLoad: true);
@@ -346,6 +357,33 @@ protected override void OnInitialized()
}
```
+Similar to the preceding example but when only simple read/write of a single value is required, the following example uses the `[SupplyParameterFromTempData]` attribute.
+
+`Pages/TempDataExample2.razor`:
+
+```razor
+@page "/tempdata-example-2"
+@inject NavigationManager NavigationManager
+
+@Message
+
+
+
+@code {
+ [SupplyParameterFromTempData]
+ public string? Message { get; set; }
+
+ private void Submit()
+ {
+ Message = "Form submitted successfully!";
+ NavigationManager.NavigateTo("/tempdata-example", forceLoad: true);
+ }
+}
+```
+
### Permanent data persistence
:::moniker-end
diff --git a/aspnetcore/blazor/tooling.md b/aspnetcore/blazor/tooling.md
index 0f07d69442d7..e9e3cf23430f 100644
--- a/aspnetcore/blazor/tooling.md
+++ b/aspnetcore/blazor/tooling.md
@@ -494,9 +494,25 @@ For more information, see the following resources:
## Blazor project templates and template options
+:::moniker range=">= aspnetcore-11.0"
+
+* Blazor Web App project template: `blazor`
+* Standalone Blazor WebAssembly app project template: `blazorwasm`
+* [Service defaults library for Blazor WebAssembly apps](#service-defaults-library-for-blazor-webassembly-apps): `blazor-wasm-servicedefaults`
+
+> [!NOTE]
+> The "Hosted" Blazor WebAssembly project template option isn't available in .NET 8 or later. To create a hosted Blazor WebAssembly app, a **Framework** option earlier than .NET 8 must be selected with the **ASP.NET Core Hosted** checkbox. However, we recommend a Blazor Web App for all new Blazor development in .NET 8 or later. For more information, see the following resources:
+>
+> *
+> *
+> *
+> *
+
+:::moniker-end
+
The Blazor framework provides project templates for creating new apps. The templates are used to create new Blazor projects and solutions regardless of the tooling that you select for Blazor development (Visual Studio, Visual Studio Code, or the [.NET command-line interface (CLI)](/dotnet/core/tools/)):
-:::moniker range=">= aspnetcore-8.0"
+:::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0"
* Blazor Web App project template: `blazor`
* Standalone Blazor WebAssembly app project template: `blazorwasm`
@@ -604,6 +620,32 @@ For more information on template options, see the following resources:
:::moniker-end
+:::moniker range=">= aspnetcore-11.0"
+
+## Service defaults library for Blazor WebAssembly apps
+
+The `blazor-wasm-servicedefaults` project template creates a service default library for Blazor WebAssembly apps with .NET Aspire integration.
+
+The template features:
+
+* [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-dotnet) support: Logging, metrics, and tracing with standard OTLP exporter.
+* Service discovery integration via the [`Microsoft.Extensions.ServiceDiscovery` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.ServiceDiscovery).
+* HTTP resilience using the standard resilience handler via the [`Microsoft.Extensions.Http.Resilience` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Http.Resilience).
+
+Example template usage with an output name of `BlazorSample.ServiceDefaults`:
+
+```dotnetcli
+dotnet new blazor-wasm-servicedefaults -o BlazorSample.ServiceDefaults
+```
+
+Reference the library from the Blazor WebAssembly client and make the following call in the app's `Program` file:
+
+```csharp
+builder.AddBlazorClientServiceDefaults();
+```
+
+:::moniker-end
+
## Additional resources
:::moniker range=">= aspnetcore-6.0"
diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
index 1bc4f75667c5..d80ed650bbd6 100644
--- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
+++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
@@ -112,14 +112,52 @@ TempData is available when .
+When supplied to a parameter for simple read/write of a single value, use the `[SupplyParameterFromTempData]` attribute:
+
+```csharp
+[SupplyParameterFromTempData]
+public string? Message { get; set; }
+```
+
+For more information, see .
### New Blazor Web Worker template (`blazorwebworker`)
Blazor WebAssembly apps can perform heavy computing on the client, but doing so on the UI thread interferes with UI rendering and negatively affects the user experience. In .NET 10, we added an article with a sample app to make offloading heavy work from the UI thread to a Web Worker easier. For .NET 11, we've added the Blazor Web Worker project template (`blazorwebworker`), which provides infrastructure for running .NET code in a Web Worker in Blazor WebAssembly apps. For more information, see .
-### `Virtualize` adapts to variable-height items at runtime
+### Virtualization enhancements
+
+* The component no longer assumes every item has the same height. The component now adapts to measured item sizes at runtime, which reduces incorrect spacing and scrolling when item heights vary. These updates include an update to the default value of , which was `3` in .NET 10 or earlier and now changes to `15` in .NET 11 or later. The change in default value increases the precision of average item height calculations.
+
+ For more information, see the *Item size* and *Overscan count* sections of .
+
+* Use the new `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added:
+
+ * `None`: No edge pinning. The viewport stays at current scroll position regardless of item changes.
+ * `Beginning`: Pins the viewport to the beginning of the list. For example, this pinning behavior is useful for a news feed user experience.
+ * `End`: Pins the viewport to the end of the list. For example, this pinning behavior is useful for a chat or logging user experience.
+
+ In the following example, the virtualized content is pinned to the beginning of the list:
+
+ ```razor
+
+ ...
+
+ ```
+
+ For more information, see .
+
+### New service defaults library project template for Blazor WebAssembly apps
+
+The `blazor-wasm-servicedefaults` project template creates a service default library for Blazor WebAssembly apps with .NET Aspire integration. For more information, see .
+
+### New development server for Blazor WebAssembly apps
+
+
-The component no longer assumes every item has the same height. The component now adapts to measured item sizes at runtime, which reduces incorrect spacing and scrolling when item heights vary. These updates include an update to the default value of , which was `3` in .NET 10 or earlier and now changes to `15` in .NET 11 or later. The change in default value increases the precision of average item height calculations.
+`Microsoft.AspNetCore.Components.Gateway` is a lightweight ASP.NET Core host that replaces [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer) for serving standalone Blazor WebAssembly applications during development and production.
-For more information, see the *Item size* and *Overscan count* sections of .
+For more information, see [[Blazor] Replace DevServer with BlazorGateway for standalone WASM apps (`dotnet/aspnetcore` #65982)](https://github.com/dotnet/aspnetcore/pull/65982).
From 15cae472f8abdfe1bbf9bec83438304484ea5d3e Mon Sep 17 00:00:00 2001
From: Luke Latham <1622880+guardrex@users.noreply.github.com>
Date: Mon, 11 May 2026 18:47:35 -0400
Subject: [PATCH 02/11] Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
aspnetcore/blazor/components/virtualization.md | 2 +-
aspnetcore/blazor/state-management/server.md | 2 +-
aspnetcore/blazor/tooling.md | 2 +-
aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 4 ++--
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md
index f8024e273485..bacccc25bb2b 100644
--- a/aspnetcore/blazor/components/virtualization.md
+++ b/aspnetcore/blazor/components/virtualization.md
@@ -276,7 +276,7 @@ The com
:::moniker range=">= aspnetcore-11.0"
-## Control viewport scroll position behavior when items are dynamically added
+## Control viewport scroll position behavior when items are dynamically added
diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md
index dab844bca532..fa9733e8aec2 100644
--- a/aspnetcore/blazor/state-management/server.md
+++ b/aspnetcore/blazor/state-management/server.md
@@ -379,7 +379,7 @@ Similar to the preceding example but when only simple read/write of a single val
private void Submit()
{
Message = "Form submitted successfully!";
- NavigationManager.NavigateTo("/tempdata-example", forceLoad: true);
+ NavigationManager.NavigateTo("/tempdata-example-2", forceLoad: true);
}
}
```
diff --git a/aspnetcore/blazor/tooling.md b/aspnetcore/blazor/tooling.md
index e9e3cf23430f..131dbc699bad 100644
--- a/aspnetcore/blazor/tooling.md
+++ b/aspnetcore/blazor/tooling.md
@@ -624,7 +624,7 @@ For more information on template options, see the following resources:
## Service defaults library for Blazor WebAssembly apps
-The `blazor-wasm-servicedefaults` project template creates a service default library for Blazor WebAssembly apps with .NET Aspire integration.
+The `blazor-wasm-servicedefaults` project template creates a service defaults library for Blazor WebAssembly apps with .NET Aspire integration.
The template features:
diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
index d80ed650bbd6..3effd39b5c56 100644
--- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
+++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
@@ -129,7 +129,7 @@ Blazor WebAssembly apps can perform heavy computing on the client, but doing so
* The component no longer assumes every item has the same height. The component now adapts to measured item sizes at runtime, which reduces incorrect spacing and scrolling when item heights vary. These updates include an update to the default value of , which was `3` in .NET 10 or earlier and now changes to `15` in .NET 11 or later. The change in default value increases the precision of average item height calculations.
- For more information, see the *Item size* and *Overscan count* sections of .
+ For more information, see the [*Item size* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#item-size) and [*Overscan count* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#overscan-count).
* Use the new `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added:
@@ -149,7 +149,7 @@ Blazor WebAssembly apps can perform heavy computing on the client, but doing so
### New service defaults library project template for Blazor WebAssembly apps
-The `blazor-wasm-servicedefaults` project template creates a service default library for Blazor WebAssembly apps with .NET Aspire integration. For more information, see .
+The `blazor-wasm-servicedefaults` project template creates a service defaults library for Blazor WebAssembly apps with .NET Aspire integration. For more information, see .
### New development server for Blazor WebAssembly apps
From 8683c341d5e82bd020b9f804297a3346900a3862 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 11 May 2026 18:48:10 -0400
Subject: [PATCH 03/11] Updates
---
aspnetcore/blazor/components/virtualization.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md
index bacccc25bb2b..a6f0c8e30260 100644
--- a/aspnetcore/blazor/components/virtualization.md
+++ b/aspnetcore/blazor/components/virtualization.md
@@ -298,7 +298,7 @@ The following example pins the viewport to the beginning of a virtualized flight
Modes can be combined. For example, assigning `Beginning | End` pins both edges. Combining `None` with other modes is supported but doesn't change the combined value.
-`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section). The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer{T}.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically.
+`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section). The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically.
:::moniker-end
From 2c678a047b5bbb04bf5bf32009cda3090d9c3fe7 Mon Sep 17 00:00:00 2001
From: Luke Latham <1622880+guardrex@users.noreply.github.com>
Date: Tue, 12 May 2026 05:50:52 -0400
Subject: [PATCH 04/11] Apply suggestions from code review
Co-authored-by: Wade Pickett
---
aspnetcore/blazor/components/virtualization.md | 2 +-
aspnetcore/blazor/state-management/server.md | 2 +-
aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md
index a6f0c8e30260..d168dcb70a2b 100644
--- a/aspnetcore/blazor/components/virtualization.md
+++ b/aspnetcore/blazor/components/virtualization.md
@@ -282,7 +282,7 @@ The com
Assign a `VirtualizeAnchorMode` value to the `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added:
-* `None`: No edge pinning. The viewport stays at current scroll position regardless of item changes.
+* `None`: No edge pinning. The viewport stays at the current scroll position regardless of item changes.
* `Beginning`: Pins the viewport to the beginning of the list. When the user is at a scroll position near the top of the list and new items arrive at the beginning, the viewport stays at the top showing the newest items. For example, this pinning behavior is useful for a news feed user experience.
* `End`: Pins the viewport to the end of the list. When the user is at a scroll position near the bottom of the list and new items arrive at the end, the viewport auto-scrolls to show them. If the user has scrolled away, auto-scroll disengages until they return to the bottom. For example, this pinning behavior is useful for a chat or logging user experience.
diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md
index fa9733e8aec2..b20e61c44515 100644
--- a/aspnetcore/blazor/state-management/server.md
+++ b/aspnetcore/blazor/state-management/server.md
@@ -324,7 +324,7 @@ In the following example, a form displays a message that's retained in `TempData
private void Submit()
{
TempData!["Message"] = "Form submitted successfully!";
- NavigationManager.NavigateTo("/tempdata-example", forceLoad: true);
+ NavigationManager.NavigateTo("/tempdata-example-1", forceLoad: true);
}
}
```
diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
index 3effd39b5c56..f1abef5693c0 100644
--- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
+++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
@@ -133,7 +133,7 @@ Blazor WebAssembly apps can perform heavy computing on the client, but doing so
* Use the new `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added:
- * `None`: No edge pinning. The viewport stays at current scroll position regardless of item changes.
+ * `None`: No edge pinning. The viewport stays at the current scroll position regardless of item changes.
* `Beginning`: Pins the viewport to the beginning of the list. For example, this pinning behavior is useful for a news feed user experience.
* `End`: Pins the viewport to the end of the list. For example, this pinning behavior is useful for a chat or logging user experience.
From 8acecadb144db646d20269f8b3f96b26c45486cb Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 12 May 2026 06:01:53 -0400
Subject: [PATCH 05/11] Update native-mobile-backend.md
---
aspnetcore/mobile/native-mobile-backend.md | 26 +++++++++++-----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/aspnetcore/mobile/native-mobile-backend.md b/aspnetcore/mobile/native-mobile-backend.md
index 79575952b28b..5869563994eb 100644
--- a/aspnetcore/mobile/native-mobile-backend.md
+++ b/aspnetcore/mobile/native-mobile-backend.md
@@ -38,9 +38,9 @@ To test it out yourself against the ASP.NET Core app created in the next section
Android emulators don't run on the local machine and use a loopback IP (10.0.2.2) to communicate with the local machine. Use .NET MAUI's [DeviceInfo](/dotnet/maui/platform-integration/device/information) class to detect the operating system the app is running on to use the correct URL.
-Navigate to the [`TodoREST`](https://github.com/dotnet/maui-samples/tree/main/9.0/WebServices/TodoREST) project and open the [`Constants.cs`](https://github.com/dotnet/maui-samples/blob/main/9.0/WebServices/TodoREST/TodoREST/Constants.cs) file. The `Constants.cs` file contains the following configuration.
+Navigate to the [`TodoREST`](https://github.com/dotnet/maui-samples/tree/main/9.0/WebServices/TodoREST) project and open the [`Constants.cs`](https://github.com/dotnet/maui-samples/blob/main/10.0/WebServices/TodoREST/TodoREST/Constants.cs) file. The `Constants.cs` file contains the following configuration.
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoREST/Constants.cs" highlight="10":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoREST/Constants.cs" highlight="10":::
You can optionally deploy the web service to a cloud service such as Azure and update the `RestUrl`.
@@ -57,27 +57,27 @@ The app should respond to all requests made over HTTPS to port 5001.
Add a model class to represent todo items. Mark required fields with the `[Required]` attribute:
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Models/TodoItem.cs":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Models/TodoItem.cs":::
API methods require defining to work with data. Use the same `ITodoRepository` interface the sample uses:
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Interfaces/ITodoRepository.cs":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Interfaces/ITodoRepository.cs":::
For this sample, the repository implementation just uses a private collection of items:
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Services/TodoRepository.cs":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Services/TodoRepository.cs":::
Configure the implementation in `Program.cs`:
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Program.cs" highlight="5":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Program.cs" highlight="5":::
## Creating the Controller
-Add a new controller to the project, [TodoItemsController](https://github.com/dotnet/maui-samples/blob/main/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs). It should inherit from . Add a `Route` attribute to indicate that the controller handles requests made to paths starting with `api/todoitems`. The `[controller]` token in the route is replaced by the name of the controller (omitting the `Controller` suffix), and is especially helpful for global routes. Learn more about [routing](../fundamentals/routing.md).
+Add a new controller to the project, [TodoItemsController](https://github.com/dotnet/maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs). It should inherit from . Add a `Route` attribute to indicate that the controller handles requests made to paths starting with `api/todoitems`. The `[controller]` token in the route is replaced by the name of the controller (omitting the `Controller` suffix), and is especially helpful for global routes. Learn more about [routing](../fundamentals/routing.md).
The controller requires an `ITodoRepository` to function; request an instance of this type through the controller's constructor. At runtime, this instance is provided using the framework's support for [dependency injection](../fundamentals/dependency-injection.md).
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDI":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDI":::
This API supports four different HTTP verbs to perform CRUD (Create, Read, Update, Delete) operations on the data source. The simplest of these is the Read operation, which corresponds to an HTTP `GET` request.
@@ -132,7 +132,7 @@ For more details on jq installation, see [jq](https://jqlang.github.io/jq/downlo
Requesting a list of items is done with a GET request to the `List` method. The `[HttpGet]` attribute on the `List` method indicates that this action should only handle GET requests. The route for this action is the route specified on the controller. You don't necessarily need to use the action name as part of the route. You just need to ensure each action has a unique and unambiguous route. Routing attributes can be applied at both the controller and method levels to build up specific routes.
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippet":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippet":::
# [macOS](#tab/macos)
@@ -195,11 +195,11 @@ By convention, creating new data items is mapped to the HTTP `POST` verb. The `C
Inside the method, the item is checked for validity and prior existence in the data store, and if no issues occur, it's added using the repository. Checking `ModelState.IsValid` performs [model validation](../mvc/models/validation.md), and should be done in every API method that accepts user input.
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetCreate":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetCreate":::
The sample uses an `enum` containing error codes that are passed to the mobile client:
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetErrorCode":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetErrorCode":::
In the terminal, test adding new items by calling the following curl command using the `POST` verb and providing the new object in JSON format in the Body of the request.
@@ -242,7 +242,7 @@ The method returns the newly created item in the response.
Modifying records is achieved using HTTP `PUT` requests. Other than this change, the `Edit` method is almost identical to `Create`. If the record isn't found, the `Edit` action returns a `NotFound` (404) response.
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetEdit":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetEdit":::
To test with curl, change the verb to `PUT`. Specify the updated object data in the Body of the request.
@@ -279,7 +279,7 @@ This method returns a `NoContent` (204) response when successful, for consistenc
Deleting records is accomplished by making `DELETE` requests to the service, and passing the ID of the item to be deleted. As with updates, requests for items that don't exist receive `NotFound` responses. Otherwise, a successful request returns a `NoContent` (204) response.
-:::code language="csharp" source="~/../maui-samples/9.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDelete":::
+:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDelete":::
Test with curl by changing the HTTP verb to `DELETE` and appending the ID of the data object to delete at the end of the URL. Nothing is required in the Body of the request.
From 663d841cebe76294ac3ae32c586ba8a89ce69740 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 12 May 2026 06:08:06 -0400
Subject: [PATCH 06/11] Update native-mobile-backend.md
---
aspnetcore/mobile/native-mobile-backend.md | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/aspnetcore/mobile/native-mobile-backend.md b/aspnetcore/mobile/native-mobile-backend.md
index 5869563994eb..f4f52db85e9a 100644
--- a/aspnetcore/mobile/native-mobile-backend.md
+++ b/aspnetcore/mobile/native-mobile-backend.md
@@ -40,7 +40,7 @@ Android emulators don't run on the local machine and use a loopback IP (10.0.2.2
Navigate to the [`TodoREST`](https://github.com/dotnet/maui-samples/tree/main/9.0/WebServices/TodoREST) project and open the [`Constants.cs`](https://github.com/dotnet/maui-samples/blob/main/10.0/WebServices/TodoREST/TodoREST/Constants.cs) file. The `Constants.cs` file contains the following configuration.
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoREST/Constants.cs" highlight="10":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoREST/Constants.cs" highlight="10":::
You can optionally deploy the web service to a cloud service such as Azure and update the `RestUrl`.
@@ -57,19 +57,19 @@ The app should respond to all requests made over HTTPS to port 5001.
Add a model class to represent todo items. Mark required fields with the `[Required]` attribute:
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Models/TodoItem.cs":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Models/TodoItem.cs":::
API methods require defining to work with data. Use the same `ITodoRepository` interface the sample uses:
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Interfaces/ITodoRepository.cs":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Interfaces/ITodoRepository.cs":::
For this sample, the repository implementation just uses a private collection of items:
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Services/TodoRepository.cs":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Services/TodoRepository.cs":::
Configure the implementation in `Program.cs`:
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Program.cs" highlight="5":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Program.cs" highlight="5":::
## Creating the Controller
@@ -77,7 +77,7 @@ Add a new controller to the project, [TodoItemsController](https://github.com/do
The controller requires an `ITodoRepository` to function; request an instance of this type through the controller's constructor. At runtime, this instance is provided using the framework's support for [dependency injection](../fundamentals/dependency-injection.md).
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDI":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDI":::
This API supports four different HTTP verbs to perform CRUD (Create, Read, Update, Delete) operations on the data source. The simplest of these is the Read operation, which corresponds to an HTTP `GET` request.
@@ -132,7 +132,7 @@ For more details on jq installation, see [jq](https://jqlang.github.io/jq/downlo
Requesting a list of items is done with a GET request to the `List` method. The `[HttpGet]` attribute on the `List` method indicates that this action should only handle GET requests. The route for this action is the route specified on the controller. You don't necessarily need to use the action name as part of the route. You just need to ensure each action has a unique and unambiguous route. Routing attributes can be applied at both the controller and method levels to build up specific routes.
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippet":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippet":::
# [macOS](#tab/macos)
@@ -195,11 +195,11 @@ By convention, creating new data items is mapped to the HTTP `POST` verb. The `C
Inside the method, the item is checked for validity and prior existence in the data store, and if no issues occur, it's added using the repository. Checking `ModelState.IsValid` performs [model validation](../mvc/models/validation.md), and should be done in every API method that accepts user input.
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetCreate":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetCreate":::
The sample uses an `enum` containing error codes that are passed to the mobile client:
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetErrorCode":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetErrorCode":::
In the terminal, test adding new items by calling the following curl command using the `POST` verb and providing the new object in JSON format in the Body of the request.
@@ -242,7 +242,7 @@ The method returns the newly created item in the response.
Modifying records is achieved using HTTP `PUT` requests. Other than this change, the `Edit` method is almost identical to `Create`. If the record isn't found, the `Edit` action returns a `NotFound` (404) response.
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetEdit":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetEdit":::
To test with curl, change the verb to `PUT`. Specify the updated object data in the Body of the request.
@@ -279,7 +279,7 @@ This method returns a `NoContent` (204) response when successful, for consistenc
Deleting records is accomplished by making `DELETE` requests to the service, and passing the ID of the item to be deleted. As with updates, requests for items that don't exist receive `NotFound` responses. Otherwise, a successful request returns a `NoContent` (204) response.
-:::code language="csharp" source="~/../maui-samples/blob/main/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDelete":::
+:::code language="csharp" source="~/../maui-samples/10.0/WebServices/TodoREST/TodoAPI/Controllers/TodoItemsController.cs" id="snippetDelete":::
Test with curl by changing the HTTP verb to `DELETE` and appending the ID of the data object to delete at the end of the URL. Nothing is required in the Body of the request.
From f1eb5957ba3241fc7092a41d5106f9acc1c9b899 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 12 May 2026 08:59:27 -0400
Subject: [PATCH 07/11] Begin to add server-triggered pause and smaller WASM
publish output content
---
aspnetcore/blazor/state-management/server.md | 230 +++++++++++++++++-
.../aspnetcore-11/includes/blazor.md | 22 +-
2 files changed, 249 insertions(+), 3 deletions(-)
diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md
index b20e61c44515..e89e31547121 100644
--- a/aspnetcore/blazor/state-management/server.md
+++ b/aspnetcore/blazor/state-management/server.md
@@ -156,6 +156,8 @@ services.AddRazorComponents()
In the preceding example, the `{CONNECTION STRING}` placeholder represents the Redis cache connection string, which should be provided using a secure approach, such as the [Secret Manager](xref:security/app-secrets#secret-manager) tool in the `Development` environment or [Azure Key Vault](/azure/key-vault/) with [Azure Managed Identities](/entra/identity/managed-identities-azure-resources/overview) for Azure-deployed apps in any environment.
+For more information on the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute), see .
+
## Pause and resume circuits
Pause and resume circuits to implement custom policies that improve the scalability of an app.
@@ -164,8 +166,8 @@ Pausing a circuit stores details about the circuit in client-side browser storag
From a JavaScript event handler:
-* Call `Blazor.pauseCircuit` to pause a circuit.
-* Call `Blazor.resumeCircuit` to resume a circuit.
+* Call `Blazor.pauseCircuit()` to pause a circuit.
+* Call `Blazor.resumeCircuit()` to resume a circuit.
The following example assumes that a circuit isn't required for an app that isn't visible:
@@ -181,6 +183,230 @@ window.addEventListener('visibilitychange', () => {
:::moniker-end
+:::moniker range=">= aspnetcore-11.0"
+
+## Server-triggered circuit pause
+
+A server-side Blazor app that adopts the Interactive Server render mode can implement server-triggered circuit pause, which allows the app to gracefully pause client circuits, preserving client state for seamless reconnection.
+
+This feature is useful in the following scenarios:
+
+* Planned shutdowns and deployments.
+* Instance draining.
+* App maintenance windows.
+
+`Circuit.RequestCircuitPauseAsync(CancellationToken)` is used to request that the connected client begin the graceful circuit-pause flow. The `CancellationToken` cancels the request before it is accepted by the framework. The method returns `true` if the request was accepted and the client was asked to begin pausing.
+
+
+
+:::moniker-end
+
:::moniker range="< aspnetcore-10.0"
## Persist state across circuits
diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
index f1abef5693c0..a4ba9eb31fa0 100644
--- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
+++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md
@@ -129,7 +129,7 @@ Blazor WebAssembly apps can perform heavy computing on the client, but doing so
* The component no longer assumes every item has the same height. The component now adapts to measured item sizes at runtime, which reduces incorrect spacing and scrolling when item heights vary. These updates include an update to the default value of , which was `3` in .NET 10 or earlier and now changes to `15` in .NET 11 or later. The change in default value increases the precision of average item height calculations.
- For more information, see the [*Item size* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#item-size) and [*Overscan count* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#overscan-count).
+ For more information, see the [*Item size* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#item-size) and [*Overscan count* section](xref:blazor/components/virtualization?view=aspnetcore-11.0#overscan-count) of the *Virtualization* article.
* Use the new `AnchorMode` parameter to control how the viewport behaves at list edges when items are dynamically added:
@@ -161,3 +161,23 @@ The `blazor-wasm-servicedefaults` project template creates a service defaults li
`Microsoft.AspNetCore.Components.Gateway` is a lightweight ASP.NET Core host that replaces [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer) for serving standalone Blazor WebAssembly applications during development and production.
For more information, see [[Blazor] Replace DevServer with BlazorGateway for standalone WASM apps (`dotnet/aspnetcore` #65982)](https://github.com/dotnet/aspnetcore/pull/65982).
+
+### Server-triggered circuit pause
+
+*This feature applies to server-side Blazor apps (Blazor Web Apps and Blazor Server apps).*
+
+Blazor already supports graceful circuit pause and resume with [`Blazor.pauseCircuit()` and `Blazor.resumeCircuit()`](xref:blazor/state-management/server#pause-and-resume-circuits). .NET 11 introduces a symmetric server-side pause and resume capability, where the server can request that connected clients begin the graceful circuit-pause flow.
+
+`Circuit.RequestCircuitPauseAsync(CancellationToken)` is used to request that the connected client begin the graceful circuit-pause flow. The `CancellationToken` cancels the request before it is accepted by the framework. The method returns `true` if the request was accepted and the client was asked to begin pausing.
+
+This feature is useful in the following scenarios:
+
+* Planned shutdowns and deployments.
+* Instance draining.
+* App maintenance windows.
+
+For more information and an example, see
+
+### Smaller Blazor WebAssembly publish output
+
+
From d19c41c605dcc7307adf29e1d265daeb2065dd10 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 12 May 2026 09:21:25 -0400
Subject: [PATCH 08/11] Virtualization updates
---
.../blazor/components/virtualization.md | 27 ++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md
index d168dcb70a2b..dcf9ba873b8c 100644
--- a/aspnetcore/blazor/components/virtualization.md
+++ b/aspnetcore/blazor/components/virtualization.md
@@ -298,7 +298,32 @@ The following example pins the viewport to the beginning of a virtualized flight
Modes can be combined. For example, assigning `Beginning | End` pins both edges. Combining `None` with other modes is supported but doesn't change the combined value.
-`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section). The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically.
+`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section).
+
+The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically. In cases where non-primative objects are virtualized and the framework can't detect if an item is prepended or appended, assign an item comparer to the `Virtualize` component:
+
+
+
+```razor
+
+ ...
+
+
+@code {
+ private static readonly IEqualityComparer itemComparer =
+ EqualityComparer.Create((a, b) =>
+ a.Index == b.Index, item => item.Index);
+
+ private async ValueTask> LoadFlights(
+ ItemsProviderRequest request)
+ {
+ ...
+ }
+}
+```
:::moniker-end
From 53e396d1c3cbba8d266795b45f1bd57bc052a7d9 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 12 May 2026 09:22:42 -0400
Subject: [PATCH 09/11] Updates
---
aspnetcore/blazor/components/virtualization.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md
index dcf9ba873b8c..fff426b50956 100644
--- a/aspnetcore/blazor/components/virtualization.md
+++ b/aspnetcore/blazor/components/virtualization.md
@@ -300,7 +300,7 @@ Modes can be combined. For example, assigning `Beginning | End` pins both edges.
`Virtualize.ItemComparer` gets or sets a comparer used to detect whether items were prepended or appended when using class-typed items with an (for more information, see the [Item provider delegate](#item-provider-delegate) section).
-The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically. In cases where non-primative objects are virtualized and the framework can't detect if an item is prepended or appended, assign an item comparer to the `Virtualize` component:
+The comparer determines if the first loaded item changed between provider calls, which indicates items were inserted at the top. For records, the default comparer's value-equality behavior (`EqualityComparer.Default`) works automatically. For an in-memory assignment, an `ItemComparer` comparer isn't required because the component can detect prepends automatically. In cases where non-primative objects are virtualized and the framework can't detect if an item is prepended or appended, assign an to the `Virtualize` component:
+-->