From 3385a0257de39113e0edf6ed7b611bdd7948b30b Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:17:07 -0400 Subject: [PATCH 01/31] [11.0 P6] Blazor Preview 6 coverage --- .../blazor/components/dynamiccomponent.md | 80 ++++++++++++++++++- aspnetcore/blazor/components/index.md | 77 +++++++++++++++--- .../call-dotnet-from-javascript.md | 12 ++- .../call-javascript-from-dotnet.md | 18 ++++- .../prerendered-state-persistence.md | 9 ++- 5 files changed, 183 insertions(+), 13 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 1048987a6761..40495d7acba8 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -5,7 +5,7 @@ description: Learn how to use dynamically-rendered Razor components in Blazor ap monikerRange: '>= aspnetcore-6.0' ms.author: wpickett ms.custom: mvc -ms.date: 11/11/2025 +ms.date: 07/08/2026 uid: blazor/components/dynamiccomponent --- # Dynamically-rendered ASP.NET Core Razor components @@ -252,6 +252,84 @@ In the following example: :::moniker-end +:::moniker range=">= aspnetcore-11.0" + +Dictionary values for [C# union-typed parameters](/dotnet/csharp/whats-new/csharp-14#union-types) must be boxed as the union, not the raw case value, because an implicit conversion is only valid at compile-time. + +Consider the following C# union type, `SlotContent`, that accepts text, a , or a : + +```csharp +using Microsoft.AspNetCore.Components; + +public union SlotContent(string Text, MarkupString Html, RenderFragment Fragment); +``` + +The following `Slot` component exposes a component parameter typed to the `SlotContent` union and renders the union's content using a [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression). In the following example, the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) specifies that `Content` is a required component parameter at design-time or build-time. + +`Slot.razor`: + +```razor +
+ @ContentSwitch() +
+ +@code { + [Parameter, EditorRequired] + public SlotContent Content { get; set; } + + private RenderFragment ContentSwitch() => Content switch + { + SlotContent.Text text => @@text.Text, + SlotContent.Html html => @@html.Html, + SlotContent.Fragment fragment => fragment.Fragment + }; +} +``` + +Unsupported: Using the preceding `Slot` component, the following component fails at run time with an : + +```razor +@using Microsoft.AspNetCore.Components + + + +@code { + [Parameter] + public Type TargetComponent { get; set; } = typeof(Slot); + + [Parameter] + public string RawTextPayload { get; set; } = "Hello from dynamic data!"; + + private Dictionary BadParameters => new() + { + { "Content", RawTextPayload } + }; +} +``` + +Supported: To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. Explicitly wrap the string in the `SlotContent` union wrapper, so the value is boxed as `SlotContent` object, preserving the exact type contract: + +```razor +@using Microsoft.AspNetCore.Components + + + +@code { + [Parameter] + public Type TargetComponent { get; set; } = typeof(Slot); + + [Parameter] + public string RawTextPayload { get; set; } = "Hello from dynamic data!"; + + private Dictionary ValidParameters => new() + { + { "Content", new SlotContent(RawTextPayload) } + }; +} +``` + +:::moniker-end + ## Event callbacks (`EventCallback`) Event callbacks () can be passed to a in its parameter dictionary. diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 9930a628b726..921629810a8f 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -1,11 +1,12 @@ --- title: ASP.NET Core Razor components +ai-usage: ai-assisted author: guardrex description: Learn how to create and use Razor components in Blazor apps, including guidance on Razor syntax, component naming, namespaces, and component parameters. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 06/24/2026 +ms.date: 07/08/2026 uid: blazor/components/index --- # ASP.NET Core Razor components @@ -850,14 +851,6 @@ Assign a C# field, property, or result of a method to a component parameter as a If the component parameter is of type string, then the attribute value is instead treated as a C# string literal. If you want to specify a C# expression instead, then use the `@` prefix. -:::moniker range=">= aspnetcore-11.0" - - - -The string-literal shortcut applies only to parameters declared as `string`. A parameter declared as a [C# union type](/dotnet/csharp/whats-new/csharp-14#union-types), even one whose cases include `string`, isn't a `string`-typed parameter, so the attribute value must be a C# expression. Use the `@` prefix, for example `Message="@("Saved.")"`. - -:::moniker-end - The following parent component displays four instances of the preceding `ParameterChild` component and sets their `Title` parameter values to: * The value of the `title` field. @@ -1171,6 +1164,72 @@ Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https:// :::moniker-end +:::moniker range=">= aspnetcore-11.0" + +Support for [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) allow a single component parameter to represent a value that's exactly one of a fixed set of types with compiler-enforced exhaustive pattern matching. A component parameter is set by direct assignment when a component is rendered from Razor markup or through . + +The following C# union type, `SlotContent`, accepts text, a , or a : + +```csharp +using Microsoft.AspNetCore.Components; + +public union SlotContent(string Text, MarkupString Html, RenderFragment Fragment); +``` + +The following `Slot` component exposes a component parameter typed to the `SlotContent` union and renders the union's content using a [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression). In the following example, the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) specifies that `Content` is a required component parameter at design-time or build-time. + +`Slot.razor`: + +```razor +
+ @ContentSwitch() +
+ +@code { + [Parameter, EditorRequired] + public SlotContent Content { get; set; } + + private RenderFragment ContentSwitch() => Content switch + { + SlotContent.Text text => @@text.Text, + SlotContent.Html html => @@html.Html, + SlotContent.Fragment fragment => fragment.Fragment + }; +} +``` + +Using the preceding `SlotContent` type and `Slot` component in a Razor component: + +```razor + + +Bold HTML")" /> + + + + + + + +@code { + private int currentCount = 0; + + private void IncrementCount() => currentCount++; +} +``` + + + +The string-literal shortcut applies only to parameters declared as `string`. A parameter declared as a C# union type, even one whose cases include `string`, isn't a `string`-typed parameter, so the attribute value must be a C# expression. Use the `@` prefix, as demonstrated by `Content="@("Simple plain text slot.")"` in the preceding example. + + + +For more information, see [Explore union types in C# 15](https://devblogs.microsoft.com/dotnet/csharp-15-union-types/). + +:::moniker-end + ## Route parameters Components can specify route parameters in the route template of the [`@page`][9] directive. The [Blazor router](xref:blazor/fundamentals/routing) uses route parameters to populate corresponding component parameters. diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index 0a9c015c88e3..399fc013682b 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -5,7 +5,7 @@ description: Learn how to invoke .NET methods from JavaScript functions in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 11/11/2025 +ms.date: 07/08/2026 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor @@ -22,6 +22,16 @@ This article explains how to invoke .NET methods from JavaScript (JS). For information on how to call JS functions from .NET, see . +:::moniker range=">= aspnetcore-11.0" + + + +> [!NOTE] +> [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) are supported. + +:::moniker-end + ## Invoke a static .NET method To invoke a static .NET method from JavaScript (JS), use the JS functions: diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index c4db66c6f5c2..d595b06bb0b9 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -5,7 +5,7 @@ description: Learn how to invoke JavaScript functions from .NET methods in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 11/11/2025 +ms.date: 07/08/2026 uid: blazor/js-interop/call-javascript-from-dotnet --- # Call JavaScript functions from .NET methods in ASP.NET Core Blazor @@ -26,12 +26,28 @@ For information on how to call .NET methods from JS, see = aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 11/11/2025 +ms.date: 07/08/2026 uid: blazor/state-management/prerendered-state-persistence --- # ASP.NET Core Blazor prerendered state persistence @@ -143,6 +143,13 @@ In the following example that serializes state for multiple components of the sa } ``` +:::moniker range=">= aspnetcore-11.0" + +> [!NOTE] +> A user-configured doesn't flow into [C# union](/dotnet/csharp/whats-new/csharp-14#union-types) deserialization. Instead, use `[JsonUnion(TypeClassifier = …)]`. + +:::moniker-end + ## Serialize state for services In the following example that serializes state for a dependency injection service: From ad5066e6347b862ee135fe4f8245f1026b14c7e6 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:56:46 -0400 Subject: [PATCH 02/31] Updates --- .../blazor/state-management/prerendered-state-persistence.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index c0c9087a8b8d..159deada03d0 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -143,6 +143,8 @@ In the following example that serializes state for multiple components of the sa } ``` +:::moniker-end + :::moniker range=">= aspnetcore-11.0" > [!NOTE] @@ -150,6 +152,8 @@ In the following example that serializes state for multiple components of the sa :::moniker-end +:::moniker range=">= aspnetcore-10.0" + ## Serialize state for services In the following example that serializes state for a dependency injection service: From 06572b4364687bc9a9fdebb590abaf4b8da663e8 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:23:55 -0400 Subject: [PATCH 03/31] Updates --- .../blazor/components/dynamiccomponent.md | 56 +++++++++--- aspnetcore/blazor/components/index.md | 86 +++++++++++++++---- aspnetcore/blazor/fundamentals/navigation.md | 2 +- aspnetcore/blazor/fundamentals/routing.md | 2 +- .../call-dotnet-from-javascript.md | 4 +- .../call-javascript-from-dotnet.md | 4 +- .../prerendered-state-persistence.md | 4 +- .../includes/parameter-binding8-10.md | 2 +- aspnetcore/mvc/models/model-binding.md | 2 +- .../includes/csharp-unions-preview-6.md | 2 +- aspnetcore/signalr/hubs.md | 2 +- 11 files changed, 126 insertions(+), 40 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 40495d7acba8..d5f5760ddac6 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -5,7 +5,7 @@ description: Learn how to use dynamically-rendered Razor components in Blazor ap monikerRange: '>= aspnetcore-6.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/08/2026 +ms.date: 07/09/2026 uid: blazor/components/dynamiccomponent --- # Dynamically-rendered ASP.NET Core Razor components @@ -254,34 +254,66 @@ In the following example: :::moniker range=">= aspnetcore-11.0" -Dictionary values for [C# union-typed parameters](/dotnet/csharp/whats-new/csharp-14#union-types) must be boxed as the union, not the raw case value, because an implicit conversion is only valid at compile-time. +Dictionary values for [C# union-typed parameters](/dotnet/csharp/language-reference/builtin-types/union) must be boxed as the union, not the raw case value, because an implicit conversion is only valid at compile-time. + + + +> [!NOTE] +> During the preview of .NET 11, the following example requires the app's project file to specify the "`preview`" C# language version: +> +> ```xml +> preview +> ``` Consider the following C# union type, `SlotContent`, that accepts text, a , or a : ```csharp using Microsoft.AspNetCore.Components; -public union SlotContent(string Text, MarkupString Html, RenderFragment Fragment); +public union SlotContent(string, MarkupString, RenderFragment); ``` -The following `Slot` component exposes a component parameter typed to the `SlotContent` union and renders the union's content using a [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression). In the following example, the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) specifies that `Content` is a required component parameter at design-time or build-time. +The following `Slot` component exposes a component parameter typed to the `SlotContent` union and renders the union's content using two implementations of the [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression), one through an assignment to a (`ContentSwitch`) and one via an inline `switch` expression. In the following example, the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) specifies that `Content` is a required component parameter at design-time or build-time. `Slot.razor`: ```razor +

@@ContentSwitch

+
@ContentSwitch()
+

Inline switch expression

+ +
+ @switch (Content) + { + case string text: + @text + break; + case MarkupString html: + @html + break; + case RenderFragment fragment: + @fragment + break; + default: + Empty slot! + break; + } +
+ @code { [Parameter, EditorRequired] public SlotContent Content { get; set; } private RenderFragment ContentSwitch() => Content switch { - SlotContent.Text text => @@text.Text, - SlotContent.Html html => @@html.Html, - SlotContent.Fragment fragment => fragment.Fragment + string text => @@text, + MarkupString html => @@html, + RenderFragment fragment => fragment, + _ => @Empty slot! }; } ``` @@ -289,7 +321,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC Unsupported: Using the preceding `Slot` component, the following component fails at run time with an : ```razor -@using Microsoft.AspNetCore.Components +@page "/slot-test-1" @@ -307,10 +339,14 @@ The following `Slot` component exposes a component parameter typed to the `SlotC } ``` -Supported: To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. Explicitly wrap the string in the `SlotContent` union wrapper, so the value is boxed as `SlotContent` object, preserving the exact type contract: +> :::no-loc text="System.InvalidCastException: Unable to cast object of type 'System.String' to type 'SlotContent'."::: + +Supported: To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. Explicitly wrap the string in the `SlotContent` union wrapper, so the value is boxed as `SlotContent` object, preserving the exact type contract. + +`SlotTest2.razor`: ```razor -@using Microsoft.AspNetCore.Components +@page "/slot-test-2" diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 921629810a8f..a4265591a353 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -6,7 +6,7 @@ description: Learn how to create and use Razor components in Blazor apps, includ monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 07/08/2026 +ms.date: 07/09/2026 uid: blazor/components/index --- # ASP.NET Core Razor components @@ -1166,57 +1166,107 @@ Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https:// :::moniker range=">= aspnetcore-11.0" -Support for [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) allow a single component parameter to represent a value that's exactly one of a fixed set of types with compiler-enforced exhaustive pattern matching. A component parameter is set by direct assignment when a component is rendered from Razor markup or through . +Support for [C# union types](/dotnet/csharp/language-reference/builtin-types/union) allow a single component parameter to represent a value that's exactly one of a fixed set of types with compiler-enforced exhaustive pattern matching. A component parameter is set by direct assignment when a component is rendered from Razor markup or through . + + + +> [!NOTE] +> During the preview of .NET 11, the following example requires the app's project file to specify the "`preview`" C# language version: +> +> ```xml +> preview +> ``` The following C# union type, `SlotContent`, accepts text, a , or a : ```csharp using Microsoft.AspNetCore.Components; -public union SlotContent(string Text, MarkupString Html, RenderFragment Fragment); +public union SlotContent(string, MarkupString, RenderFragment); ``` -The following `Slot` component exposes a component parameter typed to the `SlotContent` union and renders the union's content using a [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression). In the following example, the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) specifies that `Content` is a required component parameter at design-time or build-time. +The following `Slot` component exposes a component parameter typed to the `SlotContent` union and renders the union's content using two implementations of the [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression), one through an assignment to a (`ContentSwitch`) and one via an inline `switch` expression. In the following example, the [`[EditorRequired]` attribute](xref:Microsoft.AspNetCore.Components.EditorRequiredAttribute) specifies that `Content` is a required component parameter at design-time or build-time. `Slot.razor`: ```razor +

@@ContentSwitch

+
@ContentSwitch()
+

Inline switch expression

+ +
+ @switch (Content) + { + case string text: + @text + break; + case MarkupString html: + @html + break; + case RenderFragment fragment: + @fragment + break; + default: + Empty slot! + break; + } +
+ @code { [Parameter, EditorRequired] public SlotContent Content { get; set; } private RenderFragment ContentSwitch() => Content switch { - SlotContent.Text text => @@text.Text, - SlotContent.Html html => @@html.Html, - SlotContent.Fragment fragment => fragment.Fragment + string text => @@text, + MarkupString html => @@html, + RenderFragment fragment => fragment, + _ => @Empty slot! }; } ``` -Using the preceding `SlotContent` type and `Slot` component in a Razor component: +Using the preceding `SlotContent` type and `Slot` component in a Razor component. + +`SlotTest.razor`: ```razor - +@page "/slot-test" + +

Plain text

+ + + +

MarkupString

+ +Bold HTML slot")" /> -Bold HTML")" /> +

RenderFragment

- - - - - + @code { + private SlotContent renderFrag; + + protected override void OnInitialized() + { + renderFrag = new SlotContent((RenderFragment)(b => + { + b.OpenElement(0, "button"); + b.AddAttribute(1, "onclick", EventCallback.Factory.Create(this, Increment)); + b.AddContent(2, "Count: "); + b.AddContent(3, currentCount); + b.CloseElement(); + })); + } + private int currentCount = 0; - private void IncrementCount() => currentCount++; + private void Increment() => currentCount++; } ``` diff --git a/aspnetcore/blazor/fundamentals/navigation.md b/aspnetcore/blazor/fundamentals/navigation.md index 34c9f61099cc..c6fd30e107e7 100644 --- a/aspnetcore/blazor/fundamentals/navigation.md +++ b/aspnetcore/blazor/fundamentals/navigation.md @@ -855,7 +855,7 @@ The correct culture-invariant formatting is applied for the given type ( [!NOTE] -> [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) aren't supported by `[SupplyParameterFromQuery]` or `[SupplyParameterFromForm]`, which bind string or form values without JSON parsing. +> [C# union types](/dotnet/csharp/language-reference/builtin-types/union) aren't supported by `[SupplyParameterFromQuery]` or `[SupplyParameterFromForm]`, which bind string or form values without JSON parsing. :::moniker-end diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 81c720f9f1cf..f7f0b0bab23c 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -362,7 +362,7 @@ When the [`OnInitialized{Async}` lifecycle method](xref:blazor/components/lifecy :::moniker range=">= aspnetcore-11.0" > [!NOTE] -> Route parameters can't be typed as a [C# union](/dotnet/csharp/whats-new/csharp-14#union-types). The router parses route segments as strings and converts them to the target type without JSON parsing, so it can't dispatch to a union case. +> Route parameters can't be typed as a [C# union](/dotnet/csharp/language-reference/builtin-types/union). The router parses route segments as strings and converts them to the target type without JSON parsing, so it can't dispatch to a union case. :::moniker-end diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index 399fc013682b..62b5eb3483d7 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -5,7 +5,7 @@ description: Learn how to invoke .NET methods from JavaScript functions in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/08/2026 +ms.date: 07/09/2026 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor @@ -28,7 +28,7 @@ For information on how to call JS functions from .NET, see > [!NOTE] -> [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) are supported. +> [C# union types](/dotnet/csharp/language-reference/builtin-types/union) are supported. :::moniker-end diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index d595b06bb0b9..2223725f70dc 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -5,7 +5,7 @@ description: Learn how to invoke JavaScript functions from .NET methods in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/08/2026 +ms.date: 07/09/2026 uid: blazor/js-interop/call-javascript-from-dotnet --- # Call JavaScript functions from .NET methods in ASP.NET Core Blazor @@ -34,7 +34,7 @@ For the preceding .NET methods that invoke JS functions: * `TimeSpan` represents a time limit for a JS operation. * The `TValue` return type must also be JSON serializable. `TValue` should match the .NET type that best maps to the JSON type returned. * A [JS `Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise) is returned for `InvokeAsync` methods. `InvokeAsync` unwraps the [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise) and returns the value awaited by the [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise). -* [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) are supported. +* [C# union types](/dotnet/csharp/language-reference/builtin-types/union) are supported. :::moniker-end diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 159deada03d0..470f07057958 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -6,7 +6,7 @@ description: Learn how to persist user data (state) in Blazor apps using Blazor' monikerRange: '>= aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/08/2026 +ms.date: 07/09/2026 uid: blazor/state-management/prerendered-state-persistence --- # ASP.NET Core Blazor prerendered state persistence @@ -148,7 +148,7 @@ In the following example that serializes state for multiple components of the sa :::moniker range=">= aspnetcore-11.0" > [!NOTE] -> A user-configured doesn't flow into [C# union](/dotnet/csharp/whats-new/csharp-14#union-types) deserialization. Instead, use `[JsonUnion(TypeClassifier = …)]`. +> A user-configured doesn't flow into [C# union](/dotnet/csharp/language-reference/builtin-types/union) deserialization. Instead, use `[JsonUnion(TypeClassifier = …)]`. :::moniker-end diff --git a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8-10.md b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8-10.md index ec845eae656c..719a60357370 100644 --- a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8-10.md +++ b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8-10.md @@ -17,7 +17,7 @@ Supported binding sources: :::moniker range=">= aspnetcore-11.0" > [!NOTE] -> [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) are supported only as the body (as JSON). Non-body sources—route values, query string, headers, and form values—bind string values without JSON parsing, so they can't dispatch to a union case. +> [C# union types](/dotnet/csharp/language-reference/builtin-types/union) are supported only as the body (as JSON). Non-body sources—route values, query string, headers, and form values—bind string values without JSON parsing, so they can't dispatch to a union case. :::moniker-end diff --git a/aspnetcore/mvc/models/model-binding.md b/aspnetcore/mvc/models/model-binding.md index 11026bcfd83c..089908def98e 100644 --- a/aspnetcore/mvc/models/model-binding.md +++ b/aspnetcore/mvc/models/model-binding.md @@ -112,7 +112,7 @@ If the default source is not correct, use one of the following attributes to spe :::moniker range=">= aspnetcore-11.0" > [!NOTE] -> [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) are supported only with `[FromBody]`. The other binding sources—`[FromQuery]`, `[FromRoute]`, `[FromForm]`, and `[FromHeader]`—bind string values without JSON parsing, so they can't dispatch to a union case. +> [C# union types](/dotnet/csharp/language-reference/builtin-types/union) are supported only with `[FromBody]`. The other binding sources—`[FromQuery]`, `[FromRoute]`, `[FromForm]`, and `[FromHeader]`—bind string values without JSON parsing, so they can't dispatch to a union case. :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md b/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md index 177e6a4fce50..6621146f33cf 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md @@ -1,6 +1,6 @@ ### C# union types -ASP.NET Core supports [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) (new in .NET 11) anywhere [`System.Text.Json`](/dotnet/standard/serialization/system-text-json/overview) is used: JSON request and response bodies in Minimal APIs and MVC, SignalR's `JsonHubProtocol`, Blazor JavaScript interop, persistent component state, and prerendered component parameters. +ASP.NET Core supports [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) ([C# language reference](/dotnet/csharp/language-reference/builtin-types/union)), which are new in .NET 11, anywhere [`System.Text.Json`](/dotnet/standard/serialization/system-text-json/overview) is used: JSON request and response bodies in Minimal APIs and MVC, SignalR's `JsonHubProtocol`, Blazor JavaScript interop, persistent component state, and prerendered component parameters. ```csharp public union UnionIntString(int, string); diff --git a/aspnetcore/signalr/hubs.md b/aspnetcore/signalr/hubs.md index 1234dd2990b8..4c98120d0148 100644 --- a/aspnetcore/signalr/hubs.md +++ b/aspnetcore/signalr/hubs.md @@ -50,7 +50,7 @@ Create a hub by declaring a class that inherits from [!NOTE] -> Hub method parameters, return values, and stream items can be [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) only with the default `JsonHubProtocol`. The MessagePack and Newtonsoft.Json hub protocols don't support unions. +> Hub method parameters, return values, and stream items can be [C# union types](/dotnet/csharp/language-reference/builtin-types/union) only with the default `JsonHubProtocol`. The MessagePack and Newtonsoft.Json hub protocols don't support unions. :::moniker-end From 0709fa62b1a5c662024723bc2209dc3b4ea854c2 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:39:52 -0400 Subject: [PATCH 04/31] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- aspnetcore/blazor/components/dynamiccomponent.md | 2 +- aspnetcore/blazor/components/index.md | 6 +++--- .../state-management/prerendered-state-persistence.md | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index d5f5760ddac6..1bb1ef7e001b 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -334,7 +334,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC private Dictionary BadParameters => new() { - { "Content", RawTextPayload } + { "Content", RawTextPayload } }; } ``` diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index a4265591a353..f29c75e5db6a 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -1166,7 +1166,7 @@ Quote ©2005 [Universal Pictures](https://www.uphe.com): [Serenity](https:// :::moniker range=">= aspnetcore-11.0" -Support for [C# union types](/dotnet/csharp/language-reference/builtin-types/union) allow a single component parameter to represent a value that's exactly one of a fixed set of types with compiler-enforced exhaustive pattern matching. A component parameter is set by direct assignment when a component is rendered from Razor markup or through . +Support for [C# union types](/dotnet/csharp/language-reference/builtin-types/union) allows a single component parameter to represent a value that's exactly one of a fixed set of types with compiler-enforced exhaustive pattern matching. A component parameter is set by direct assignment when a component is rendered from Razor markup or through . @@ -1243,7 +1243,7 @@ Using the preceding `SlotContent` type and `Slot` component in a Razor component

MarkupString

-Bold HTML slot")" /> +

RenderFragment

@@ -1272,7 +1272,7 @@ Using the preceding `SlotContent` type and `Slot` component in a Razor component -The string-literal shortcut applies only to parameters declared as `string`. A parameter declared as a C# union type, even one whose cases include `string`, isn't a `string`-typed parameter, so the attribute value must be a C# expression. Use the `@` prefix, as demonstrated by `Content="@("Simple plain text slot.")"` in the preceding example. +The string-literal shortcut applies only to parameters declared as `string`. A parameter declared as a C# union type, even one whose cases include `string`, isn't a `string`-typed parameter, so the attribute value must be a C# expression. Use the `@` prefix, as demonstrated by `Content="@("Simple plain text slot")"` in the preceding example. diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 470f07057958..8cdea376095c 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -148,8 +148,7 @@ In the following example that serializes state for multiple components of the sa :::moniker range=">= aspnetcore-11.0" > [!NOTE] -> A user-configured doesn't flow into [C# union](/dotnet/csharp/language-reference/builtin-types/union) deserialization. Instead, use `[JsonUnion(TypeClassifier = …)]`. - +> A user-configured doesn't flow into [C# union](/dotnet/csharp/language-reference/builtin-types/union) deserialization. Instead, use `[JsonUnion(TypeClassifier = typeof({TYPE CLASSIFIER}))]`. :::moniker-end :::moniker range=">= aspnetcore-10.0" From 3f0aa0d05ec534a1a6f62ca792acfb65d778413d Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:40:52 -0400 Subject: [PATCH 05/31] Updates --- .../blazor/state-management/prerendered-state-persistence.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 8cdea376095c..d2fb32fca5f9 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -148,7 +148,8 @@ In the following example that serializes state for multiple components of the sa :::moniker range=">= aspnetcore-11.0" > [!NOTE] -> A user-configured doesn't flow into [C# union](/dotnet/csharp/language-reference/builtin-types/union) deserialization. Instead, use `[JsonUnion(TypeClassifier = typeof({TYPE CLASSIFIER}))]`. +> A user-configured doesn't flow into [C# union](/dotnet/csharp/language-reference/builtin-types/union) deserialization. Instead, use `[JsonUnion(TypeClassifier = typeof({TYPE CLASSIFIER}))]`, where the `{TYPE CLASSIFIER}` placeholder is the type classifier. + :::moniker-end :::moniker range=">= aspnetcore-10.0" From 9da4ca363221a67dc0ba102528138f73a417ad74 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:21:23 -0400 Subject: [PATCH 06/31] Updates --- aspnetcore/blazor/components/dynamiccomponent.md | 5 +++-- aspnetcore/blazor/components/index.md | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 1bb1ef7e001b..7de13d57966f 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -1,11 +1,12 @@ --- title: Dynamically-rendered ASP.NET Core Razor components +ai-usage: ai-assisted author: guardrex description: Learn how to use dynamically-rendered Razor components in Blazor apps. monikerRange: '>= aspnetcore-6.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/09/2026 +ms.date: 07/10/2026 uid: blazor/components/dynamiccomponent --- # Dynamically-rendered ASP.NET Core Razor components @@ -341,7 +342,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC > :::no-loc text="System.InvalidCastException: Unable to cast object of type 'System.String' to type 'SlotContent'."::: -Supported: To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. Explicitly wrap the string in the `SlotContent` union wrapper, so the value is boxed as `SlotContent` object, preserving the exact type contract. +Supported: To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. `SlotTest2.razor`: diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index f29c75e5db6a..4299ab36dc3b 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -6,7 +6,7 @@ description: Learn how to create and use Razor components in Blazor apps, includ monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 07/09/2026 +ms.date: 07/10/2026 uid: blazor/components/index --- # ASP.NET Core Razor components @@ -1230,7 +1230,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC } ``` -Using the preceding `SlotContent` type and `Slot` component in a Razor component. +The following `SlotTest` component uses the preceding `SlotContent` type and `Slot` component. `SlotTest.razor`: From 87a326a3188e2dad39fc1cdb12804a8c90966bd9 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:25:47 -0400 Subject: [PATCH 07/31] Updates --- .../aspnetcore-11/includes/csharp-unions-preview-6.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md b/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md index 6621146f33cf..4d2cb0fcffab 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md @@ -1,6 +1,6 @@ ### C# union types -ASP.NET Core supports [C# union types](/dotnet/csharp/whats-new/csharp-14#union-types) ([C# language reference](/dotnet/csharp/language-reference/builtin-types/union)), which are new in .NET 11, anywhere [`System.Text.Json`](/dotnet/standard/serialization/system-text-json/overview) is used: JSON request and response bodies in Minimal APIs and MVC, SignalR's `JsonHubProtocol`, Blazor JavaScript interop, persistent component state, and prerendered component parameters. +ASP.NET Core supports [C# union types](/dotnet/csharp/whats-new/csharp-15#union-types) ([C# language reference](/dotnet/csharp/language-reference/builtin-types/union)), which are new in .NET 11, anywhere [`System.Text.Json`](/dotnet/standard/serialization/system-text-json/overview) is used: JSON request and response bodies in Minimal APIs and MVC, SignalR's `JsonHubProtocol`, Blazor JavaScript interop, persistent component state, and prerendered component parameters. ```csharp public union UnionIntString(int, string); From 57168163ae85e805dff4fed61ac6f38d9731b9b8 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 10 Jul 2026 09:43:23 -0400 Subject: [PATCH 08/31] Updates --- aspnetcore/blazor/components/index.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 4299ab36dc3b..8238f4767c97 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -1230,12 +1230,12 @@ The following `Slot` component exposes a component parameter typed to the `SlotC } ``` -The following `SlotTest` component uses the preceding `SlotContent` type and `Slot` component. +The following `SlotExample` component uses the preceding `SlotContent` type and `Slot` component. -`SlotTest.razor`: +`SlotExample.razor`: ```razor -@page "/slot-test" +@page "/slot-example"

Plain text

@@ -1247,14 +1247,14 @@ The following `SlotTest` component uses the preceding `SlotContent` type and `Sl

RenderFragment

- + @code { - private SlotContent renderFrag; + private SlotContent content; protected override void OnInitialized() { - renderFrag = new SlotContent((RenderFragment)(b => + content = new SlotContent((RenderFragment)(b => { b.OpenElement(0, "button"); b.AddAttribute(1, "onclick", EventCallback.Factory.Create(this, Increment)); From f5aec5b4c046d815667ddc5e2d3e8cf9d5fa1a21 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:49:01 -0400 Subject: [PATCH 09/31] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- aspnetcore/blazor/components/dynamiccomponent.md | 2 +- aspnetcore/blazor/components/index.md | 2 +- .../javascript-interoperability/call-dotnet-from-javascript.md | 2 +- .../javascript-interoperability/call-javascript-from-dotnet.md | 2 +- .../blazor/state-management/prerendered-state-persistence.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 7de13d57966f..9d876584c845 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -279,7 +279,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC `Slot.razor`: ```razor -

@@ContentSwitch

+

ContentSwitch

@ContentSwitch() diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 8238f4767c97..cdbe0543c7c7 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -1190,7 +1190,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC `Slot.razor`: ```razor -

@@ContentSwitch

+

ContentSwitch

@ContentSwitch() diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index 62b5eb3483d7..aebccfd3c596 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -5,7 +5,7 @@ description: Learn how to invoke .NET methods from JavaScript functions in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/09/2026 +ms.date: 07/10/2026 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index 2223725f70dc..a2251dc7247d 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -5,7 +5,7 @@ description: Learn how to invoke JavaScript functions from .NET methods in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/09/2026 +ms.date: 07/10/2026 uid: blazor/js-interop/call-javascript-from-dotnet --- # Call JavaScript functions from .NET methods in ASP.NET Core Blazor diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index d2fb32fca5f9..f8a6ae94fdf7 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -6,7 +6,7 @@ description: Learn how to persist user data (state) in Blazor apps using Blazor' monikerRange: '>= aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/09/2026 +ms.date: 07/10/2026 uid: blazor/state-management/prerendered-state-persistence --- # ASP.NET Core Blazor prerendered state persistence From 6c107c888653f2b853bc2243c42b85a9d1f3a603 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:59:52 -0400 Subject: [PATCH 10/31] EnvironmentBoundary renamed to EnvironmentView --- aspnetcore/blazor/fundamentals/environments.md | 18 +++++++++--------- .../aspnetcore-11/includes/blazor.md | 18 +++++++++--------- .../includes/csharp-unions-preview-6.md | 4 +++- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/environments.md b/aspnetcore/blazor/fundamentals/environments.md index 479b23872d45..d4590459e255 100644 --- a/aspnetcore/blazor/fundamentals/environments.md +++ b/aspnetcore/blazor/fundamentals/environments.md @@ -291,30 +291,30 @@ App settings from the `appsettings.{ENVIRONMENT}.json` file are loaded by the ap :::moniker range=">= aspnetcore-11.0" -## `EnvironmentBoundary` component +## `EnvironmentView` component -Use the `EnvironmentBoundary` component for conditional rendering based on the hosting environment. This component provides a consistent way to render content based on the current environment across both server-side and client-side hosting models. +Use the `EnvironmentView` component for conditional rendering based on the hosting environment. This component provides a consistent way to render content based on the current environment across both server-side and client-side hosting models. -The `EnvironmentBoundary` component accepts `Include` and `Exclude` parameters for specifying environment names. The component performs case-insensitive matching. +The `EnvironmentView` component accepts `Include` and `Exclude` parameters for specifying environment names. The component performs case-insensitive matching. ```razor @using Microsoft.AspNetCore.Components.Web - +
Debug mode enabled
-
+ - +

Pre-production environment

-
+ - +

@DateTime.Now

-
+ ``` :::moniker-end diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index a9bc3283889d..205be59a8f1b 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -430,28 +430,28 @@ A new `GetUriWithHash` extension method permits `NavigationManager` to easily co The method uses `string.Create` for optimal performance and works correctly with non-root base URIs (for example, when using ``). -### `EnvironmentBoundary` component +### `EnvironmentView` component -Blazor now includes a built-in `EnvironmentBoundary` component for conditional rendering based on the hosting environment. This component provides a consistent way to render content based on the current environment across both server-side and client-side hosting models. +Blazor now includes a built-in `EnvironmentView` component for conditional rendering based on the hosting environment. This component provides a consistent way to render content based on the current environment across both server-side and client-side hosting models. -The `EnvironmentBoundary` component accepts `Include` and `Exclude` parameters for specifying environment names. The component performs case-insensitive matching and follows the same semantics as MVC's `EnvironmentTagHelper`. +The `EnvironmentView` component accepts `Include` and `Exclude` parameters for specifying environment names. The component performs case-insensitive matching and follows the same semantics as MVC's `EnvironmentTagHelper`. ```razor @using Microsoft.AspNetCore.Components.Web - +
Debug mode enabled
-
+ - +

Pre-production environment

-
+ - +

@DateTime.Now

-
+ ``` ### MathML namespace support diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md b/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md index 4d2cb0fcffab..db6a9dea622f 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/csharp-unions-preview-6.md @@ -8,4 +8,6 @@ public union UnionIntString(int, string); app.MapGet("/value", () => new UnionIntString(42)); ``` -Union types aren't supported for non-body binding sources such as route values, query strings, headers, and form fields. \ No newline at end of file +Union types aren't supported for non-body binding sources such as route values, query strings, headers, and form fields. + +For examples and additional information that apply to Blazor apps, see the [Component parameters section in the Components overview article](xref:blazor/components/index#component-parameters) and the [Pass parameters section of the Dynamically-rendered ASP.NET Core Razor components article](xref:blazor/components/dynamiccomponent). From 559bda5393834d3967deea7efa86c9cb65de66b3 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 10:05:28 -0400 Subject: [PATCH 11/31] GetUriWithHash renamed to GetUriWithFragment --- aspnetcore/blazor/fundamentals/navigation.md | 6 +++--- aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/navigation.md b/aspnetcore/blazor/fundamentals/navigation.md index c6fd30e107e7..39abdb6ed901 100644 --- a/aspnetcore/blazor/fundamentals/navigation.md +++ b/aspnetcore/blazor/fundamentals/navigation.md @@ -1182,7 +1182,7 @@ For more information on JavaScript isolation with JavaScript modules, see -Use the `GetUriWithHash` extension method to construct a URI with a hash fragment. This helper method provides an efficient, zero-allocation way to append hash fragments to the current URI. The following example demonstrates two use cases: +Use the `GetUriWithFragment` extension method to construct a URI with a hash fragment. This helper method provides an efficient, zero-allocation way to append hash fragments to the current URI. The following example demonstrates two use cases: * Inline call that jumps to Section 1 (`id="section-1"`) of the rendered page. * Method call that receives a section identifier (`sectionId`) and navigates to the section of the page. @@ -1190,14 +1190,14 @@ Use the `GetUriWithHash` extension method to construct a URI with a hash fragmen ```razor @inject NavigationManager Navigation - + Jump to Section 1 @code { private void NavigateToSection(string sectionId) { - var uri = Navigation.GetUriWithHash(sectionId); + var uri = Navigation.GetUriWithFragment(sectionId); Navigation.NavigateTo(uri); } } diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 205be59a8f1b..9e34b8845ba2 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -405,9 +405,9 @@ public string? FlashMessage { get; set; } For more information, see . -### `GetUriWithHash` extension method +### `GetUriWithFragment` extension method -A new `GetUriWithHash` extension method permits `NavigationManager` to easily construct URIs with hash fragments. This helper method provides an efficient, zero-allocation way to append hash fragments to the current URI. The following example demonstrates two use cases: +A new `GetUriWithFragment` extension method permits `NavigationManager` to easily construct URIs with hash fragments. This helper method provides an efficient, zero-allocation way to append hash fragments to the current URI. The following example demonstrates two use cases: * Inline call that jumps to Section 1 (`id="section-1"`) of the rendered page. * Method call that receives a section Id (`sectionId`) and navigates to the section of the page. @@ -415,14 +415,14 @@ A new `GetUriWithHash` extension method permits `NavigationManager` to easily co ```razor @inject NavigationManager Navigation - + Jump to Section 1 @code { private void NavigateToSection(string sectionId) { - var uri = Navigation.GetUriWithHash(sectionId); + var uri = Navigation.GetUriWithFragment(sectionId); Navigation.NavigateTo(uri); } } From fc7434844cd7e76d12aece8a17bc33bda82652b5 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 10:12:31 -0400 Subject: [PATCH 12/31] Rename disableDomPreservation to preserveDom (with opposite meaning) --- aspnetcore/blazor/fundamentals/startup.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index e5144de1ab7e..d135d32dbd1a 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -1067,6 +1067,27 @@ Standalone Blazor WebAssembly: *This section applies to Blazor Web Apps.* +:::moniker-end + +:::moniker range ">= aspnetcore-11.0" + +To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `PreserveDom` to `false` for `Blazor.start`: + +```html + + +``` + +**In the preceding example, the `{BLAZOR SCRIPT}` placeholder is the Blazor script path and file name.** For the location of the script, see . + +:::moniker-end + +:::moniker range ">= aspnetcore-8.0 < aspnetcore-11.0" + To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `disableDomPreservation` to `true` for `Blazor.start`: ```html From 813cd55f4f4b144015459a02958358b4c14d7e70 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 10:53:47 -0400 Subject: [PATCH 13/31] Fixes to TempData and `[SupplyParameterFromSession]` persistence for streaming SSR --- aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 9e34b8845ba2..2bbaed753ce2 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -738,3 +738,9 @@ Complete feature coverage is available in the following articles: For more information, see [Add localization support to Microsoft.Extensions.Validation (`dotnet/aspnetcore` #66646)](https://github.com/dotnet/aspnetcore/pull/66646). Please don't comment on closed issues and PRs. If you have feedback on this feature, please open a new issue on the `dotnet/aspnetcore` GitHub repository. + +### Fixes to TempData and `[SupplyParameterFromSession]` persistence for streaming SSR + +When a page uses session-backed features, where a component has a `[SupplyParameterFromSession]` parameter (which creates a subscription) or the session-storage TempData provider is active, the session cookie (`.AspNetCore.Session`) is now issued before streaming begins, even if no value is ultimately written. Pages that don't use session-backed features are unaffected. + +For more information, see [Fix TempData and SupplyParameterFromSession persistence for streaming SSR case (`dotnet/aspnetcore` #66832)](https://github.com/dotnet/aspnetcore/pull/66832) (Please don't comment on closed issues and PRs.). From 8515716e89e84821dca8334e8713fb589dffc865 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 11:27:37 -0400 Subject: [PATCH 14/31] Virtualization: Content Security Policy (CSP) compliance --- aspnetcore/blazor/components/virtualization.md | 15 ++++++++++++++- aspnetcore/blazor/fundamentals/startup.md | 2 +- .../middleware/request-decompression.md | 6 +++--- aspnetcore/fundamentals/url-rewriting.md | 2 +- aspnetcore/includes/problem-details-service.md | 2 +- aspnetcore/performance/response-compression.md | 2 +- .../aspnetcore-11/includes/blazor.md | 9 +++++++++ .../configure-jwt-bearer-authentication.md | 2 +- 8 files changed, 31 insertions(+), 9 deletions(-) diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md index 0e5b1cc8b1e0..45e2bb8bc343 100644 --- a/aspnetcore/blazor/components/virtualization.md +++ b/aspnetcore/blazor/components/virtualization.md @@ -5,7 +5,7 @@ description: Learn how to use component virtualization in ASP.NET Core Blazor ap monikerRange: '>= aspnetcore-5.0' ms.author: wpickett ms.custom: mvc -ms.date: 11/11/2025 +ms.date: 07/13/2026 uid: blazor/components/virtualization --- # ASP.NET Core Razor component virtualization @@ -472,3 +472,16 @@ In the preceding example, the document root is used as the scroll container, so * :::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +## Content Security Policy (CSP) compliance + +The `Virtualize` component renders dynamic inline `style` attributes on its spacer and placeholder elements because spacer heights are calculated at runtime based on scroll position, item count, and average item size, which change on every scroll interaction. + +To avoid CSP violations, `Virtualize` components: + +* Render CSS styles in a `data-blazor-style` attribute instead of a `style` attribute. +* Use a JS [`MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver) to read the attribute's value and apply each declaration via the [CSS Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model): `element.style.setProperty(name, value)`. + +:::moniker-end diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index d135d32dbd1a..d8d1ea153f86 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -5,7 +5,7 @@ description: Learn how to configure Blazor startup. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 11/11/2025 +ms.date: 07/13/2026 uid: blazor/fundamentals/startup --- # ASP.NET Core Blazor startup diff --git a/aspnetcore/fundamentals/middleware/request-decompression.md b/aspnetcore/fundamentals/middleware/request-decompression.md index 95c0187ce960..4332283dcd2a 100644 --- a/aspnetcore/fundamentals/middleware/request-decompression.md +++ b/aspnetcore/fundamentals/middleware/request-decompression.md @@ -58,7 +58,7 @@ The `Content-Encoding` header values that the request decompression middleware s :::moniker range=">= aspnetcore-7.0 < aspnetcore-11.0" -| [`Content-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) header values | Description | +| [`Content-Encoding`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Encoding) header values | Description | | --------- | --------- | | `br` | [Brotli compressed data format](https://tools.ietf.org/html/rfc7932) | | `deflate` | [DEFLATE compressed data format](https://tools.ietf.org/html/rfc1951) | @@ -68,7 +68,7 @@ The `Content-Encoding` header values that the request decompression middleware s :::moniker range=">= aspnetcore-11.0" -| [`Content-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) header values | Description | +| [`Content-Encoding`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Encoding) header values | Description | | --------- | --------- | | `br` | [Brotli compressed data format](https://tools.ietf.org/html/rfc7932) | | `deflate` | [DEFLATE compressed data format](https://tools.ietf.org/html/rfc1951) | @@ -112,7 +112,7 @@ In order of precedence, the maximum request size for an endpoint is set by: ## Additional Resources * -* [Mozilla Developer Network: Content-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) +* [Mozilla Developer Network: Content-Encoding](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Encoding) * [Brotli Compressed Data Format](https://www.rfc-editor.org/rfc/rfc7932) * [DEFLATE Compressed Data Format Specification version 1.3](https://www.rfc-editor.org/rfc/rfc1951) * [GZIP file format specification version 4.3](https://www.rfc-editor.org/rfc/rfc1952) diff --git a/aspnetcore/fundamentals/url-rewriting.md b/aspnetcore/fundamentals/url-rewriting.md index 769699b32443..9ac083d9cb19 100644 --- a/aspnetcore/fundamentals/url-rewriting.md +++ b/aspnetcore/fundamentals/url-rewriting.md @@ -44,7 +44,7 @@ When redirecting requests to a different URL, indicate whether the redirect is p * The [`301 - Moved Permanently`](https://developer.mozilla.org/docs/Web/HTTP/Status/301) status code is used where the resource has a new, permanent URL and that all future requests for the resource should use the new URL. *The client may cache and reuse the response when a 301 status code is received.* -* The [`302 - Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302) status code is used where the redirection is temporary or generally subject to change. The 302 status code indicates to the client not to store the URL and use it in the future. +* The [`302 - Found`](https://developer.mozilla.org/docs/Web/HTTP/Status/302) status code is used where the redirection is temporary or generally subject to change. The 302 status code indicates to the client not to store the URL and use it in the future. For more information on status codes, see [RFC 9110: Status Code Definitions](https://www.rfc-editor.org/rfc/rfc9110#name-status-codes). diff --git a/aspnetcore/includes/problem-details-service.md b/aspnetcore/includes/problem-details-service.md index 8adfde44015a..ef4b77168ae6 100644 --- a/aspnetcore/includes/problem-details-service.md +++ b/aspnetcore/includes/problem-details-service.md @@ -2,7 +2,7 @@ The problem details service implements the interface, which supports creating problem details in ASP.NET Core. The extension method on registers the default `IProblemDetailsService` implementation. -In ASP.NET Core apps, the following middleware generates problem details HTTP responses when `AddProblemDetails` is called, except when the [`Accept` request HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) doesn't include one of the content types supported by the registered (default: `application/json`): +In ASP.NET Core apps, the following middleware generates problem details HTTP responses when `AddProblemDetails` is called, except when the [`Accept` request HTTP header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept) doesn't include one of the content types supported by the registered (default: `application/json`): * : Generates a problem details response when a custom handler is not defined. * : Generates a problem details response by default. diff --git a/aspnetcore/performance/response-compression.md b/aspnetcore/performance/response-compression.md index 7c739b046eb2..7077d357354c 100644 --- a/aspnetcore/performance/response-compression.md +++ b/aspnetcore/performance/response-compression.md @@ -251,7 +251,7 @@ Replace or append MIME types with the [ResponseCompressionOptions.MimeTypes](xre ## Add the Vary header -When responses are compressed based on the [Accept-Encoding request header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding), there can be uncompressed and multiple compressed versions of the response. To instruct client and proxy caches that multiple versions exist and should be stored, the `Vary` header is added with an `Accept-Encoding` value. The response middleware [adds the 'Vary' header](https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs#L198-L241) in the _ResponseCompressionBody.cs_ file automatically when the response is compressed. +When responses are compressed based on the [Accept-Encoding request header](https://developer.mozilla.org/docs/Web/HTTP/Reference/Headers/Accept-Encoding), there can be uncompressed and multiple compressed versions of the response. To instruct client and proxy caches that multiple versions exist and should be stored, the `Vary` header is added with an `Accept-Encoding` value. The response middleware [adds the 'Vary' header](https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs#L198-L241) in the _ResponseCompressionBody.cs_ file automatically when the response is compressed. [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 2bbaed753ce2..d40b39f2d630 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -171,6 +171,15 @@ For more information, see the following resources: * * [[release/11.0-preview4] Virtualization AnchorMode with variable-height support (`dotnet/aspnetcore` #66521)](https://github.com/dotnet/aspnetcore/pull/66521) (Please don't comment on closed issues and PRs.) +* Content Security Policy (CSP) compliance + + The `Virtualize` component renders dynamic inline `style` attributes on its spacer and placeholder elements (for example, `style="height: 478896px; flex-shrink: 0;`) because spacer heights are calculated at runtime based on scroll position, item count, and average item size, which change on every scroll interaction. These are blocked by a Content Security Policy (CSP) when `style-src 'self'` is set, breaking virtualization entirely for apps with strict CSP policies. + + Now, CSP violations are avoided because `Virtualize` components: + + * Render CSS styles in a `data-blazor-style` attribute instead of a `style` attribute. + * Use a JS [`MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver) to read the attribute's value and apply each declaration via the [CSS Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model): `element.style.setProperty(name, value)`. + ### New service defaults library project template for Blazor WebAssembly apps The `blazor-wasm-servicedefaults` project template creates a service defaults library for Blazor WebAssembly apps with [Aspire](/dotnet/aspire/get-started/aspire-overview) integration. For more information, see . diff --git a/aspnetcore/security/authentication/configure-jwt-bearer-authentication.md b/aspnetcore/security/authentication/configure-jwt-bearer-authentication.md index ee30f5e5f19c..6190aad940a9 100644 --- a/aspnetcore/security/authentication/configure-jwt-bearer-authentication.md +++ b/aspnetcore/security/authentication/configure-jwt-bearer-authentication.md @@ -97,7 +97,7 @@ The [OAuth specifications](/entra/identity-platform/access-token-claims-referenc ### 403 Forbidden -A [403 Forbidden](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) response typically indicates that the authenticated user lacks the necessary permissions to access the requested resource. This is distinct from authentication issues, e.g. an invalid token, and is unrelated to the standard claims within the access token. +A [403 Forbidden](https://developer.mozilla.org/docs/Web/HTTP/Status/403) response typically indicates that the authenticated user lacks the necessary permissions to access the requested resource. This is distinct from authentication issues, e.g. an invalid token, and is unrelated to the standard claims within the access token. In ASP.NET Core, you can enforce authorization using: From ade5f551d4de7ea5251a5df1a2cdf33bff37aff1 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 12:09:39 -0400 Subject: [PATCH 15/31] Redundant `app.UseAntiforgery()` removed from Blazor Web templates --- aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index d40b39f2d630..4b10152b48fc 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -688,7 +688,7 @@ For more information, see [Add built-in support for async form validation in Bla Please don't comment on closed issues and PRs. If you have feedback on this feature, please open a new issue on the `dotnet/aspnetcore` GitHub repository. -## Blazor and Minimal APIs support error localization +### Blazor and Minimal APIs support error localization Validation of Blazor forms and Minimal API endpoints receives first-class support for localization of error messages and property names. By default, localization uses language-specific RESX files deployed as part of the assembly. @@ -753,3 +753,9 @@ Please don't comment on closed issues and PRs. If you have feedback on this feat When a page uses session-backed features, where a component has a `[SupplyParameterFromSession]` parameter (which creates a subscription) or the session-storage TempData provider is active, the session cookie (`.AspNetCore.Session`) is now issued before streaming begins, even if no value is ultimately written. Pages that don't use session-backed features are unaffected. For more information, see [Fix TempData and SupplyParameterFromSession persistence for streaming SSR case (`dotnet/aspnetcore` #66832)](https://github.com/dotnet/aspnetcore/pull/66832) (Please don't comment on closed issues and PRs.). + +### Redundant `app.UseAntiforgery()` removed from Blazor Web templates + +[CSRF protection](xref:security/csrf-protection) is now on by default via the auto-injected CSRF Protection Middleware, so the explicit `app.UseAntiforgery()` call in Blazor Web templates has been removed. + +For more information, see . From ff16f4fcba0e57fdcda5ad2d76353d3c60fd2914 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:20:16 -0400 Subject: [PATCH 16/31] Preloading and enhanced nav -AND- automatic CSRF middleware --- aspnetcore/blazor/forms/index.md | 21 +++++++++++++++++ .../blazor/fundamentals/static-files.md | 7 ++++++ aspnetcore/blazor/security/index.md | 23 ++++++++++++++++++- ...blazor-enhanced-nav-preloading-disabled.md | 4 +++- .../aspnetcore-10/includes/blazor.md | 7 ++++++ .../aspnetcore-11/includes/blazor.md | 5 +++- 6 files changed, 64 insertions(+), 3 deletions(-) diff --git a/aspnetcore/blazor/forms/index.md b/aspnetcore/blazor/forms/index.md index e99e60f1ffb7..ba9c03cc03eb 100644 --- a/aspnetcore/blazor/forms/index.md +++ b/aspnetcore/blazor/forms/index.md @@ -289,8 +289,29 @@ There's no need to call is called in the `Program` file. +:::monkier-end + +:::moniker range=">= aspnetcore-11.0" + +Automatic Cross-Site Request Forgery (CSRF) Protection Middleware is enabled by default in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. + +If the app explicitly adds Antiforgery Middleware by calling in its request processing pipeline in the `Program` file, is called after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . + +For more information, see . + +> [!IMPORTANT] +> The following guidance on the component and the service only apply to an app that explicitly adopts token-based Antiforgery Middleware by calling in its request processing pipeline. + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0" + The app uses Antiforgery Middleware by calling in its request processing pipeline in the `Program` file. is called after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . +:::moniker-end + +:::moniker range=">= aspnetcore-8.0" + The component renders an antiforgery token as a hidden field, and the `[RequireAntiforgeryToken]` attribute enables antiforgery protection. If an antiforgery check fails, a [`400 - Bad Request`](https://developer.mozilla.org/docs/Web/HTTP/Status/400) response is thrown and the form isn't processed. For forms based on , the component and `[RequireAntiforgeryToken]` attribute are automatically added to provide antiforgery protection. diff --git a/aspnetcore/blazor/fundamentals/static-files.md b/aspnetcore/blazor/fundamentals/static-files.md index 016e972957c8..253f4ed7c783 100644 --- a/aspnetcore/blazor/fundamentals/static-files.md +++ b/aspnetcore/blazor/fundamentals/static-files.md @@ -44,6 +44,13 @@ In standalone Blazor WebAssembly apps, framework assets are scheduled for high p :::moniker-end +:::moniker range=">= aspnetcore-11.0" + +> [!NOTE] +> Blazor Web App enhanced navigation doesn't attempt to preload resources in .NET 11 or later. For more information, see [Blazor enhanced navigation no longer preloads resources](/aspnet/core/breaking-changes/11/blazor-enhanced-nav-preloading-disabled). + +:::moniker-end + ## Static asset delivery in server-side Blazor apps :::moniker range=">= aspnetcore-9.0" diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index 77341d01f323..3edee44e39d0 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -68,11 +68,32 @@ For Microsoft Azure services, we recommend using *managed identities*. Managed i ## Antiforgery support -The Blazor template: +The Blazor project template: + +:::moniker-end + +:::moniker range=">= aspnetcore-11.0" + + +* Automatic Cross-Site Request Forgery (CSRF) Protection Middleware is enabled by default in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. +* When is explicitly called in the `Program` file, token-based antiforgery services are added to the app. is called after . A call to must be placed after calls, if present, to and . + +For more information, see . + +> [!IMPORTANT] +> The following guidance on the component and the service only apply to an app that explicitly adopts token-based Antiforgery Middleware by calling in its request processing pipeline. + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0" * Adds antiforgery services automatically when is called in the `Program` file. * Adds Antiforgery Middleware by calling in its request processing pipeline in the `Program` file and requires endpoint [antiforgery protection](xref:security/anti-request-forgery) to mitigate the threats of Cross-Site Request Forgery (CSRF/XSRF). is called after . A call to must be placed after calls, if present, to and . +:::moniker-end + +:::moniker range=">= aspnetcore-8.0" + The component renders an antiforgery token as a hidden field, and this component is automatically added to form () instances. For more information, see . The service provides access to an antiforgery token associated with the current session. Inject the service and call its method to obtain the current . For more information, see . diff --git a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md index 1a8e09b9e57e..510542d905dd 100644 --- a/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md +++ b/aspnetcore/breaking-changes/11/blazor-enhanced-nav-preloading-disabled.md @@ -30,7 +30,9 @@ This change is a [behavioral change](/dotnet/core/compatibility/categories#behav ## Reason for change -The implicit preload hints during enhanced navigation produced redundant browser preload requests because the WebAssembly runtime doesn't restart and the DOM-merge step often produced an unstable order of `` elements. Removing the hints for enhanced navigation gives more predictable network behavior. For more information, see [dotnet/aspnetcore#63544](https://github.com/dotnet/aspnetcore/pull/63544). +The implicit preload hints during enhanced navigation produced redundant browser preload requests because the WebAssembly runtime doesn't restart and the DOM-merge step often produced an unstable order of `` elements. Removing the hints for enhanced navigation gives more predictable network behavior. + +For more information, see [[Blazor] Disable preloading for enhanced navigation (`dotnet/aspnetcore` #63544)](https://github.com/dotnet/aspnetcore/pull/63544), and please don't comment on the closed PR. If you have feedback on this change, open a new issue on the `dotnet/aspnetcore` GitHub repository. ## Recommended action diff --git a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md index 99f476139e52..15c5512a0224 100644 --- a/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-10/includes/blazor.md @@ -231,6 +231,13 @@ In standalone Blazor WebAssembly apps, framework assets are scheduled for high p For more information, see . +:::moniker range=">= aspnetcore-11.0" + +> [!NOTE] +> This feature is disabled for Blazor Web App enhanced navigation in .NET 11 or later. For more information, see [Blazor enhanced navigation no longer preloads resources](/aspnet/core/breaking-changes/11/blazor-enhanced-nav-preloading-disabled). + +:::moniker-end + ### Set the environment in standalone Blazor WebAssembly apps The `Blazor-Environment` header and `Properties/launchSettings.json` file (`ASPNETCORE_ENVIRONMENT` environment variable) are no longer used to control the environment in standalone Blazor WebAssembly apps. diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 4b10152b48fc..2cf969ede0bf 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -758,4 +758,7 @@ For more information, see [Fix TempData and SupplyParameterFromSession persisten [CSRF protection](xref:security/csrf-protection) is now on by default via the auto-injected CSRF Protection Middleware, so the explicit `app.UseAntiforgery()` call in Blazor Web templates has been removed. -For more information, see . +For more information, see the following resources: + +* +* [Blazor server-side rendering defers antiforgery validation to middleware (breaking change announcement)](/aspnet/core/breaking-changes/11/blazor-server-side-rendering-deferred-cross-site-request-forgery-protection) From 8181f9aef7ff894152476242b7e0a6425e0cc8fd Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:24:31 -0400 Subject: [PATCH 17/31] Updates --- aspnetcore/blazor/forms/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/forms/index.md b/aspnetcore/blazor/forms/index.md index ba9c03cc03eb..a5572f8b563f 100644 --- a/aspnetcore/blazor/forms/index.md +++ b/aspnetcore/blazor/forms/index.md @@ -289,7 +289,7 @@ There's no need to call is called in the `Program` file. -:::monkier-end +:::moniker-end :::moniker range=">= aspnetcore-11.0" From e93e34e80e7b266676b555ed1e8ca1c00328be78 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:49:04 -0400 Subject: [PATCH 18/31] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Luke Latham <1622880+guardrex@users.noreply.github.com> --- aspnetcore/blazor/components/dynamiccomponent.md | 2 +- aspnetcore/blazor/components/index.md | 2 +- aspnetcore/blazor/fundamentals/startup.md | 6 +++--- .../call-dotnet-from-javascript.md | 2 +- .../call-javascript-from-dotnet.md | 2 +- .../state-management/prerendered-state-persistence.md | 2 +- aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 9d876584c845..0876bbb61852 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -6,7 +6,7 @@ description: Learn how to use dynamically-rendered Razor components in Blazor ap monikerRange: '>= aspnetcore-6.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/10/2026 +ms.date: 07/13/2026 uid: blazor/components/dynamiccomponent --- # Dynamically-rendered ASP.NET Core Razor components diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index cdbe0543c7c7..63f9a6bd9013 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -6,7 +6,7 @@ description: Learn how to create and use Razor components in Blazor apps, includ monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 07/10/2026 +ms.date: 07/13/2026 uid: blazor/components/index --- # ASP.NET Core Razor components diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index d8d1ea153f86..1f07ce9f3743 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -1069,9 +1069,9 @@ Standalone Blazor WebAssembly: :::moniker-end -:::moniker range ">= aspnetcore-11.0" +:::moniker range=">= aspnetcore-11.0" -To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `PreserveDom` to `false` for `Blazor.start`: +To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `preserveDom` to `false` for `Blazor.start`: ```html @@ -1086,7 +1086,7 @@ To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navi :::moniker-end -:::moniker range ">= aspnetcore-8.0 < aspnetcore-11.0" +:::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0" To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `disableDomPreservation` to `true` for `Blazor.start`: diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index aebccfd3c596..64ac11a14944 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -5,7 +5,7 @@ description: Learn how to invoke .NET methods from JavaScript functions in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/10/2026 +ms.date: 07/13/2026 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index a2251dc7247d..7c8f67012676 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -5,7 +5,7 @@ description: Learn how to invoke JavaScript functions from .NET methods in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/10/2026 +ms.date: 07/13/2026 uid: blazor/js-interop/call-javascript-from-dotnet --- # Call JavaScript functions from .NET methods in ASP.NET Core Blazor diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index f8a6ae94fdf7..6d22c4fae623 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -6,7 +6,7 @@ description: Learn how to persist user data (state) in Blazor apps using Blazor' monikerRange: '>= aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/10/2026 +ms.date: 07/13/2026 uid: blazor/state-management/prerendered-state-persistence --- # ASP.NET Core Blazor prerendered state persistence diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 2cf969ede0bf..6ec84541234d 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -173,7 +173,7 @@ For more information, see the following resources: * Content Security Policy (CSP) compliance - The `Virtualize` component renders dynamic inline `style` attributes on its spacer and placeholder elements (for example, `style="height: 478896px; flex-shrink: 0;`) because spacer heights are calculated at runtime based on scroll position, item count, and average item size, which change on every scroll interaction. These are blocked by a Content Security Policy (CSP) when `style-src 'self'` is set, breaking virtualization entirely for apps with strict CSP policies. + The `Virtualize` component renders dynamic inline `style` attributes on its spacer and placeholder elements (for example, `style="height: 478896px; flex-shrink: 0;"`) because spacer heights are calculated at runtime based on scroll position, item count, and average item size, which change on every scroll interaction. These are blocked by a Content Security Policy (CSP) when `style-src 'self'` is set, breaking virtualization entirely for apps with strict CSP policies. Now, CSP violations are avoided because `Virtualize` components: @@ -752,7 +752,7 @@ Please don't comment on closed issues and PRs. If you have feedback on this feat When a page uses session-backed features, where a component has a `[SupplyParameterFromSession]` parameter (which creates a subscription) or the session-storage TempData provider is active, the session cookie (`.AspNetCore.Session`) is now issued before streaming begins, even if no value is ultimately written. Pages that don't use session-backed features are unaffected. -For more information, see [Fix TempData and SupplyParameterFromSession persistence for streaming SSR case (`dotnet/aspnetcore` #66832)](https://github.com/dotnet/aspnetcore/pull/66832) (Please don't comment on closed issues and PRs.). +For more information, see [Fix TempData and SupplyParameterFromSession persistence for streaming SSR case (`dotnet/aspnetcore` #66832)](https://github.com/dotnet/aspnetcore/pull/66832). (Please don't comment on closed issues and PRs.) ### Redundant `app.UseAntiforgery()` removed from Blazor Web templates From 2871d6fc113aee6e90deec9290cdfc800dba2f34 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:51:14 -0400 Subject: [PATCH 19/31] Updates --- aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 6ec84541234d..ca791035cab1 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -756,7 +756,7 @@ For more information, see [Fix TempData and SupplyParameterFromSession persisten ### Redundant `app.UseAntiforgery()` removed from Blazor Web templates -[CSRF protection](xref:security/csrf-protection) is now on by default via the auto-injected CSRF Protection Middleware, so the explicit `app.UseAntiforgery()` call in Blazor Web templates has been removed. +[CSRF protection](xref:security/csrf-protection) is enabled by default via the auto-injected CSRF Protection Middleware, so the explicit `app.UseAntiforgery()` call in Blazor Web App templates has been removed. For more information, see the following resources: From cd5340e79fad7c2abb33093ad028125a18c81c13 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 09:26:03 -0400 Subject: [PATCH 20/31] React to Virtualize CSP feedback --- .../blazor/components/dynamiccomponent.md | 2 +- aspnetcore/blazor/components/index.md | 2 +- .../blazor/components/virtualization.md | 37 ++++++++++++++++--- aspnetcore/blazor/fundamentals/startup.md | 2 +- .../call-dotnet-from-javascript.md | 2 +- .../call-javascript-from-dotnet.md | 2 +- .../prerendered-state-persistence.md | 2 +- 7 files changed, 38 insertions(+), 11 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 0876bbb61852..1e5f7e21c15b 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -6,7 +6,7 @@ description: Learn how to use dynamically-rendered Razor components in Blazor ap monikerRange: '>= aspnetcore-6.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/components/dynamiccomponent --- # Dynamically-rendered ASP.NET Core Razor components diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 63f9a6bd9013..a100dca0a7b1 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -6,7 +6,7 @@ description: Learn how to create and use Razor components in Blazor apps, includ monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/components/index --- # ASP.NET Core Razor components diff --git a/aspnetcore/blazor/components/virtualization.md b/aspnetcore/blazor/components/virtualization.md index 45e2bb8bc343..cec21977844a 100644 --- a/aspnetcore/blazor/components/virtualization.md +++ b/aspnetcore/blazor/components/virtualization.md @@ -5,7 +5,7 @@ description: Learn how to use component virtualization in ASP.NET Core Blazor ap monikerRange: '>= aspnetcore-5.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/components/virtualization --- # ASP.NET Core Razor component virtualization @@ -365,6 +365,26 @@ If your source code looks like the following: At runtime, the component renders a DOM structure similar to the following: +:::moniker-end + +:::moniker range=">= aspnetcore-11.0" + +```html +
+ +
Flight 12
+
Flight 13
+
Flight 14
+
Flight 15
+
Flight 16
+ +
+``` + +:::moniker-end + +:::moniker range="< aspnetcore-11.0" + ```html
@@ -377,6 +397,10 @@ At runtime, the ``` +:::moniker-end + +:::moniker range=">= aspnetcore-6.0" + The actual number of rows rendered and the size of the spacers vary according to your styling and `Items` collection size. However, notice that there are spacer `div` elements injected before and after your content. These serve two purposes: * To provide an offset before and after your content, causing currently-visible items to appear at the correct location in the scroll range and the scroll range itself to represent the total size of all content. @@ -477,11 +501,14 @@ In the preceding example, the document root is used as the scroll container, so ## Content Security Policy (CSP) compliance -The `Virtualize` component renders dynamic inline `style` attributes on its spacer and placeholder elements because spacer heights are calculated at runtime based on scroll position, item count, and average item size, which change on every scroll interaction. +The `Virtualize` component renders dynamic inline `style` attributes on its spacer elements because spacer heights are calculated at runtime based on scroll position, item count, and average item size, which change on every scroll interaction. -To avoid CSP violations, `Virtualize` components: +To avoid CSP violations, render CSS height in a `data-blazor-virtualize-reserved-height` attribute instead of a `style` attribute, which makes the rendered component compatible with strict [Content Security Policy (CSP)](https://developer.mozilla.org/docs/Web/HTTP/Guides/CSP) configurations. -* Render CSS styles in a `data-blazor-style` attribute instead of a `style` attribute. -* Use a JS [`MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver) to read the attribute's value and apply each declaration via the [CSS Object Model (CSSOM)](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model): `element.style.setProperty(name, value)`. +In the following example, the height is set to 3,400 pixels: + +```razor + +``` :::moniker-end diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index 1f07ce9f3743..5df6daf192ca 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -5,7 +5,7 @@ description: Learn how to configure Blazor startup. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/fundamentals/startup --- # ASP.NET Core Blazor startup diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index 64ac11a14944..8c897fa2f352 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -5,7 +5,7 @@ description: Learn how to invoke .NET methods from JavaScript functions in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index 7c8f67012676..be25911d64ed 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -5,7 +5,7 @@ description: Learn how to invoke JavaScript functions from .NET methods in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: wpickett ms.custom: mvc, sfi-ropc-nochange -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/js-interop/call-javascript-from-dotnet --- # Call JavaScript functions from .NET methods in ASP.NET Core Blazor diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 6d22c4fae623..aa0bf4d15889 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -6,7 +6,7 @@ description: Learn how to persist user data (state) in Blazor apps using Blazor' monikerRange: '>= aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 07/13/2026 +ms.date: 07/14/2026 uid: blazor/state-management/prerendered-state-persistence --- # ASP.NET Core Blazor prerendered state persistence From 0b2269336ed62e60bbe30ae4da25513a578f859a Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:29:13 -0400 Subject: [PATCH 21/31] Updates --- aspnetcore/blazor/security/index.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index 3edee44e39d0..63244c804572 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -74,9 +74,8 @@ The Blazor project template: :::moniker range=">= aspnetcore-11.0" - -* Automatic Cross-Site Request Forgery (CSRF) Protection Middleware is enabled by default in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. -* When is explicitly called in the `Program` file, token-based antiforgery services are added to the app. is called after . A call to must be placed after calls, if present, to and . +* Enables automatic Cross-Site Request Forgery (CSRF) Protection Middleware in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. +* Adds token-based antiforgery services to the app when is called in the `Program` file. is called after . A call to must be placed after calls, if present, to and . For more information, see . From e15f30836d79e067958cc3f325069551aeba504a Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:31:06 -0400 Subject: [PATCH 22/31] Updates --- aspnetcore/blazor/security/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index 63244c804572..7e6f2a74ce2f 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -75,7 +75,10 @@ The Blazor project template: :::moniker range=">= aspnetcore-11.0" * Enables automatic Cross-Site Request Forgery (CSRF) Protection Middleware in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. -* Adds token-based antiforgery services to the app when is called in the `Program` file. is called after . A call to must be placed after calls, if present, to and . +* Adds token-based antiforgery services implicitly to the app when is called in the `Program` file. + +> [!NOTE] +> If is explicitly called, call after . A call to must be placed after calls, if present, to and . For more information, see . From f8d91611f614d7856f0665620777fc246c9765bb Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:34:19 -0400 Subject: [PATCH 23/31] Updates --- aspnetcore/blazor/forms/index.md | 5 ++++- aspnetcore/blazor/security/index.md | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/aspnetcore/blazor/forms/index.md b/aspnetcore/blazor/forms/index.md index a5572f8b563f..c18befb58381 100644 --- a/aspnetcore/blazor/forms/index.md +++ b/aspnetcore/blazor/forms/index.md @@ -295,10 +295,13 @@ Antiforgery services are automatically added to Blazor apps when in its request processing pipeline in the `Program` file, is called after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . +Token-based antiforgery services are implicitly added to the app when is called in the `Program` file. For more information, see . +> [!NOTE] +> If the app explicitly adds Antiforgery Middleware by calling in its request processing pipeline in the `Program` file, is called after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . + > [!IMPORTANT] > The following guidance on the component and the service only apply to an app that explicitly adopts token-based Antiforgery Middleware by calling in its request processing pipeline. diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index 7e6f2a74ce2f..aea384a10f6b 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -75,13 +75,13 @@ The Blazor project template: :::moniker range=">= aspnetcore-11.0" * Enables automatic Cross-Site Request Forgery (CSRF) Protection Middleware in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. -* Adds token-based antiforgery services implicitly to the app when is called in the `Program` file. +* Implicitly adds token-based antiforgery services to the app when is called in the `Program` file. + +For more information, see . > [!NOTE] > If is explicitly called, call after . A call to must be placed after calls, if present, to and . -For more information, see . - > [!IMPORTANT] > The following guidance on the component and the service only apply to an app that explicitly adopts token-based Antiforgery Middleware by calling in its request processing pipeline. From 6056a9e6e707946ffaae149ff39aa51533826dc4 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:23:04 -0400 Subject: [PATCH 24/31] Reverting blazor/fundamentals/startup.md changes --- aspnetcore/blazor/fundamentals/startup.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index 5df6daf192ca..bc768a88d127 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -1067,27 +1067,6 @@ Standalone Blazor WebAssembly: *This section applies to Blazor Web Apps.* -:::moniker-end - -:::moniker range=">= aspnetcore-11.0" - -To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `preserveDom` to `false` for `Blazor.start`: - -```html - - -``` - -**In the preceding example, the `{BLAZOR SCRIPT}` placeholder is the Blazor script path and file name.** For the location of the script, see . - -:::moniker-end - -:::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0" - To disable [enhanced navigation and form handling](xref:blazor/fundamentals/navigation#enhanced-navigation-and-form-handling), set `disableDomPreservation` to `true` for `Blazor.start`: ```html From 71c2b83fc49a7bbffa0908bc2fe91157b9afc148 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:44:46 -0400 Subject: [PATCH 25/31] Configure Blazor client behavior from the server --- .../aspnetcore-11/includes/blazor.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index ca791035cab1..0879b5ad91b6 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -762,3 +762,37 @@ For more information, see the following resources: * * [Blazor server-side rendering defers antiforgery validation to middleware (breaking change announcement)](/aspnet/core/breaking-changes/11/blazor-server-side-rendering-deferred-cross-site-request-forgery-protection) + +### Configure Blazor client behavior from the server + + + +Blazor apps can now configure client-side startup behavior from the server in C# when mapping Razor components instead of hand-writing `Blazor.start` JavaScript. `WithBrowserOptions` sets options that the server serializes into the rendered page and the Blazor script applies in the browser, across the Server, WebAssembly, and Auto render modes. The options cover the client log level, interactive Server reconnection, whether enhanced navigation preserves the DOM, and a WebAssembly runtime's environment name, culture, and environment variables: + +```csharp +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .WithBrowserOptions(options => + { + options.LogLevel = LogLevel.Warning; + options.Server.ReconnectionMaxRetries = 10; + options.Server.ReconnectionRetryInterval = TimeSpan.FromSeconds(1.5); + options.Ssr.PreserveDom = true; + options.WebAssembly.EnvironmentName = "Staging"; + options.WebAssembly.EnvironmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = + "https://localhost:4318"; + }); +``` + +You can also set the options from a component with `` or read the resolved options from `HttpContext` with `GetBrowserOptions()`. + +The API was introduced in Preview 4 and reshaped in Preview 6 to follow options conventions. If you adopted the earlier shape, `WithBrowserConfiguration` is now `WithBrowserOptions`, `BrowserConfiguration` is now `BrowserOptions`, `ServerBrowserOptions` is now `InteractiveServerBrowserOptions`, `SsrBrowserOptions.DisableDomPreservation` is now `PreserveDom` (with the opposite meaning), and `CircuitInactivityTimeoutMs` is now `CircuitInactivityTimeout` (a `TimeSpan`). + +For more information, see the following resources: + +* [API Proposal: BrowserOptions for server-to-client configuration (`dotnet/aspnetcore` #66393)](https://github.com/dotnet/aspnetcore/issues/66393) +* [Reshape BrowserConfiguration API per review (BrowserOptions) while preserving the JS wire format (`dotnet/aspnetcore` #67337)](https://github.com/dotnet/aspnetcore/pull/67337) + +Please don't comment on closed issues and PRs. Open a new issue to provide feedback on this API. From c6bd13570d3ec2353fde0529131caac83b55d5d4 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:47:07 -0400 Subject: [PATCH 26/31] Move the Configure Blazor client behavior from the server section UP --- .../aspnetcore-11/includes/blazor.md | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 0879b5ad91b6..adf990705237 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -588,6 +588,40 @@ builder.Services.AddHostedService(); Hosted services are started when the app starts and stopped when it shuts down, providing a clean lifecycle for background operations in Blazor WebAssembly apps. +### Configure Blazor client behavior from the server + + + +Blazor apps can now configure client-side startup behavior from the server in C# when mapping Razor components instead of hand-writing `Blazor.start` JavaScript. `WithBrowserOptions` sets options that the server serializes into the rendered page and the Blazor script applies in the browser, across the Server, WebAssembly, and Auto render modes. The options cover the client log level, interactive Server reconnection, whether enhanced navigation preserves the DOM, and a WebAssembly runtime's environment name, culture, and environment variables: + +```csharp +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .WithBrowserOptions(options => + { + options.LogLevel = LogLevel.Warning; + options.Server.ReconnectionMaxRetries = 10; + options.Server.ReconnectionRetryInterval = TimeSpan.FromSeconds(1.5); + options.Ssr.PreserveDom = true; + options.WebAssembly.EnvironmentName = "Staging"; + options.WebAssembly.EnvironmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = + "https://localhost:4318"; + }); +``` + +You can also set the options from a component with `` or read the resolved options from `HttpContext` with `GetBrowserOptions()`. + +The API was introduced in Preview 4 and reshaped in Preview 6 to follow options conventions. If you adopted the earlier shape, `WithBrowserConfiguration` is now `WithBrowserOptions`, `BrowserConfiguration` is now `BrowserOptions`, `ServerBrowserOptions` is now `InteractiveServerBrowserOptions`, `SsrBrowserOptions.DisableDomPreservation` is now `PreserveDom` (with the opposite meaning), and `CircuitInactivityTimeoutMs` is now `CircuitInactivityTimeout` (a `TimeSpan`). + +For more information, see the following resources: + +* [API Proposal: BrowserOptions for server-to-client configuration (`dotnet/aspnetcore` #66393)](https://github.com/dotnet/aspnetcore/issues/66393) +* [Reshape BrowserConfiguration API per review (BrowserOptions) while preserving the JS wire format (`dotnet/aspnetcore` #67337)](https://github.com/dotnet/aspnetcore/pull/67337) + +Please don't comment on closed issues and PRs. Open a new issue to provide feedback on this API. + ### Environment variables in Blazor WebAssembly configuration Blazor WebAssembly applications can now access environment variables through `IConfiguration`. This enables runtime configuration without rebuilding the application, making it easier to deploy the same build to different environments. @@ -762,37 +796,3 @@ For more information, see the following resources: * * [Blazor server-side rendering defers antiforgery validation to middleware (breaking change announcement)](/aspnet/core/breaking-changes/11/blazor-server-side-rendering-deferred-cross-site-request-forgery-protection) - -### Configure Blazor client behavior from the server - - - -Blazor apps can now configure client-side startup behavior from the server in C# when mapping Razor components instead of hand-writing `Blazor.start` JavaScript. `WithBrowserOptions` sets options that the server serializes into the rendered page and the Blazor script applies in the browser, across the Server, WebAssembly, and Auto render modes. The options cover the client log level, interactive Server reconnection, whether enhanced navigation preserves the DOM, and a WebAssembly runtime's environment name, culture, and environment variables: - -```csharp -app.MapRazorComponents() - .AddInteractiveServerRenderMode() - .WithBrowserOptions(options => - { - options.LogLevel = LogLevel.Warning; - options.Server.ReconnectionMaxRetries = 10; - options.Server.ReconnectionRetryInterval = TimeSpan.FromSeconds(1.5); - options.Ssr.PreserveDom = true; - options.WebAssembly.EnvironmentName = "Staging"; - options.WebAssembly.EnvironmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = - "https://localhost:4318"; - }); -``` - -You can also set the options from a component with `` or read the resolved options from `HttpContext` with `GetBrowserOptions()`. - -The API was introduced in Preview 4 and reshaped in Preview 6 to follow options conventions. If you adopted the earlier shape, `WithBrowserConfiguration` is now `WithBrowserOptions`, `BrowserConfiguration` is now `BrowserOptions`, `ServerBrowserOptions` is now `InteractiveServerBrowserOptions`, `SsrBrowserOptions.DisableDomPreservation` is now `PreserveDom` (with the opposite meaning), and `CircuitInactivityTimeoutMs` is now `CircuitInactivityTimeout` (a `TimeSpan`). - -For more information, see the following resources: - -* [API Proposal: BrowserOptions for server-to-client configuration (`dotnet/aspnetcore` #66393)](https://github.com/dotnet/aspnetcore/issues/66393) -* [Reshape BrowserConfiguration API per review (BrowserOptions) while preserving the JS wire format (`dotnet/aspnetcore` #67337)](https://github.com/dotnet/aspnetcore/pull/67337) - -Please don't comment on closed issues and PRs. Open a new issue to provide feedback on this API. From 2b72e4a6bd2573c99387a745766883b373278d93 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:52:32 -0400 Subject: [PATCH 27/31] Add Blazor Virtualize can scroll to an item to What's New coverage --- .../aspnetcore-11/includes/blazor.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index adf990705237..4eaa3bb3187c 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -796,3 +796,31 @@ For more information, see the following resources: * * [Blazor server-side rendering defers antiforgery validation to middleware (breaking change announcement)](/aspnet/core/breaking-changes/11/blazor-server-side-rendering-deferred-cross-site-request-forgery-protection) + +### Blazor Virtualize can scroll to an item + + + +The `Virtualize` component can now open at a specific item and scroll to any item on demand. Two new public APIs make this possible: + +* `InitialIndex` positions the list at a given item on the first interactive render, so the list opens at that item without a flash of the first item. +* `ScrollToIndexAsync(int itemIndex, CancellationToken cancellationToken = default)` scrolls to an item at any time after the first render and returns a `Task` that completes when the target is aligned to the top of the viewport. + +```razor + +
@context.Name
+
+ + + +@code { + private Virtualize list = default!; + private List products = ProductCatalog.All; + + private async Task GoToTop() => await list.ScrollToIndexAsync(0); +} +``` + +Out-of-range indexes are clamped to the valid range. If a second `ScrollToIndexAsync` call starts while one is still in flight, the last call wins. Calling `ScrollToIndexAsync` before the first interactive render throws `InvalidOperationException`; use `InitialIndex` to set the starting position instead. + +For more information, see [Add `InitialIndex` parameter and `ScrollToIndexAsync` API to `Virtualize` (`dotnet/aspnetcore` #66753)](https://github.com/dotnet/aspnetcore/pull/66753). (Please don't comment on closed issues and PRs.) From 0cb5bb01c9f1d600cd691f84d555a57d57a667fd Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:04:03 -0400 Subject: [PATCH 28/31] Updates to BWA template change for antiforgery --- aspnetcore/release-notes/aspnetcore-11/includes/blazor.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md index 4eaa3bb3187c..5124ae64cde8 100644 --- a/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-11/includes/blazor.md @@ -788,7 +788,7 @@ When a page uses session-backed features, where a component has a `[SupplyParame For more information, see [Fix TempData and SupplyParameterFromSession persistence for streaming SSR case (`dotnet/aspnetcore` #66832)](https://github.com/dotnet/aspnetcore/pull/66832). (Please don't comment on closed issues and PRs.) -### Redundant `app.UseAntiforgery()` removed from Blazor Web templates +### Redundant `app.UseAntiforgery()` removed from Blazor Web App templates [CSRF protection](xref:security/csrf-protection) is enabled by default via the auto-injected CSRF Protection Middleware, so the explicit `app.UseAntiforgery()` call in Blazor Web App templates has been removed. @@ -797,6 +797,12 @@ For more information, see the following resources: * * [Blazor server-side rendering defers antiforgery validation to middleware (breaking change announcement)](/aspnet/core/breaking-changes/11/blazor-server-side-rendering-deferred-cross-site-request-forgery-protection) +General coverage for the new automatic CSRF protection in ASP.NET Core: + +* +* +* + ### Blazor Virtualize can scroll to an item From 64b7044fc5324f5b5233e26a850747a9414ae5ec Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:20:46 -0400 Subject: [PATCH 29/31] Union coverage updates --- .../blazor/components/dynamiccomponent.md | 25 +------------------ aspnetcore/blazor/components/index.md | 5 ++++ 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index 1e5f7e21c15b..82000876b1ba 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -319,30 +319,7 @@ The following `Slot` component exposes a component parameter typed to the `SlotC } ``` -Unsupported: Using the preceding `Slot` component, the following component fails at run time with an : - -```razor -@page "/slot-test-1" - - - -@code { - [Parameter] - public Type TargetComponent { get; set; } = typeof(Slot); - - [Parameter] - public string RawTextPayload { get; set; } = "Hello from dynamic data!"; - - private Dictionary BadParameters => new() - { - { "Content", RawTextPayload } - }; -} -``` - -> :::no-loc text="System.InvalidCastException: Unable to cast object of type 'System.String' to type 'SlotContent'."::: - -Supported: To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. +To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates. `SlotTest2.razor`: diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index a100dca0a7b1..1a51676902e6 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -1274,6 +1274,11 @@ The following `SlotExample` component uses the preceding `SlotContent` type and The string-literal shortcut applies only to parameters declared as `string`. A parameter declared as a C# union type, even one whose cases include `string`, isn't a `string`-typed parameter, so the attribute value must be a C# expression. Use the `@` prefix, as demonstrated by `Content="@("Simple plain text slot")"` in the preceding example. + + +Populating a union-typed parameter via a [child content render fragment](#child-content-render-fragments) (`...`) isn't supported at this time but might be introduced in a future preview release. + For more information, see [Explore union types in C# 15](https://devblogs.microsoft.com/dotnet/csharp-15-union-types/). From a48d605e278d382d209c15231c70332b9963d556 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:35:20 -0400 Subject: [PATCH 30/31] React to feedback --- aspnetcore/blazor/forms/index.md | 9 ++++----- aspnetcore/blazor/security/index.md | 13 ++++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/aspnetcore/blazor/forms/index.md b/aspnetcore/blazor/forms/index.md index c18befb58381..9b327c062179 100644 --- a/aspnetcore/blazor/forms/index.md +++ b/aspnetcore/blazor/forms/index.md @@ -293,14 +293,13 @@ Antiforgery services are automatically added to Blazor apps when is called in the `Program` file. +Token-based antiforgery services are added to the app when is called in the `Program` file. -For more information, see . +To explicitly add Antiforgery Middleware, call after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . -> [!NOTE] -> If the app explicitly adds Antiforgery Middleware by calling in its request processing pipeline in the `Program` file, is called after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . +For more information, see . > [!IMPORTANT] > The following guidance on the component and the service only apply to an app that explicitly adopts token-based Antiforgery Middleware by calling in its request processing pipeline. diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index aea384a10f6b..c3bbfff47791 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -74,23 +74,22 @@ The Blazor project template: :::moniker range=">= aspnetcore-11.0" -* Enables automatic Cross-Site Request Forgery (CSRF) Protection Middleware in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. -* Implicitly adds token-based antiforgery services to the app when is called in the `Program` file. +* Enables automatic Cross-Site Request Forgery (CSRF) protection middleware in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. +* Implicitly adds token-based antiforgery services to the app when is called in the `Program` file. Antiforgery middleware isn't automatically included in the request processing pipeline without explicitly calling . -For more information, see . +To explicitly add antiforgery middleware, call after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . -> [!NOTE] -> If is explicitly called, call after . A call to must be placed after calls, if present, to and . +For more information, see . > [!IMPORTANT] -> The following guidance on the component and the service only apply to an app that explicitly adopts token-based Antiforgery Middleware by calling in its request processing pipeline. +> The following guidance on the component and the service only apply to an app that explicitly adopts token-based antiforgery middleware by calling in its request processing pipeline. :::moniker-end :::moniker range=">= aspnetcore-8.0 < aspnetcore-11.0" * Adds antiforgery services automatically when is called in the `Program` file. -* Adds Antiforgery Middleware by calling in its request processing pipeline in the `Program` file and requires endpoint [antiforgery protection](xref:security/anti-request-forgery) to mitigate the threats of Cross-Site Request Forgery (CSRF/XSRF). is called after . A call to must be placed after calls, if present, to and . +* Adds antiforgery middleware by calling in its request processing pipeline in the `Program` file and requires endpoint [antiforgery protection](xref:security/anti-request-forgery) to mitigate the threats of Cross-Site Request Forgery (CSRF/XSRF). is called after . A call to must be placed after calls, if present, to and . :::moniker-end From 7c5615bcc2420b8c141394acdac88e80288fc117 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 15 Jul 2026 09:07:02 -0400 Subject: [PATCH 31/31] Add additional CSRF/antiforgery middleware coverage --- aspnetcore/blazor/forms/index.md | 22 +++++++++++++++++++++- aspnetcore/blazor/security/index.md | 26 ++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/aspnetcore/blazor/forms/index.md b/aspnetcore/blazor/forms/index.md index 9b327c062179..848401696551 100644 --- a/aspnetcore/blazor/forms/index.md +++ b/aspnetcore/blazor/forms/index.md @@ -295,10 +295,30 @@ Antiforgery services are automatically added to Blazor apps when is called in the `Program` file. +Token-based antiforgery services are added to the app when is called in the `Program` file. However, token validation only runs when antiforgery middleware is explicitly added to the request processing pipeline by calling . + +Adding token-based antiforgery middleware doesn't replace the automatic header-based CSRF protection middleware. When an app calls , both defense mechanisms run for a form post: + +* The header-based CSRF protection middleware runs first and records its verdict. +* Antiforgery middleware performs token-based validation. + +The token-based result from antiforgery middleware is authoritative and overrides the earlier header-based CSRF middleware verdict. To explicitly add Antiforgery Middleware, call after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . +To disable the automatic header-based CSRF protection middleware, set the `DisableCsrfProtection` configuration key to true. For example, use the app settings file (`appsettings.json`) to disable the middleware: + +```json +{ + "DisableCsrfProtection": true +} +``` + +The the `DisableCsrfProtection` configuration setting can be supplied by any configuration source, including via an environment variable (`DisableCsrfProtection=true`). + +> [!WARNING] +> Disabling the automatic CSRF protection middleware removes the default header-based (`Sec-Fetch-Site`/`Origin`) protection for the entire app. Only disable it if you provide an alternative CSRF defense, such as explicitly adopting token-based antiforgery middleware by calling . + For more information, see . > [!IMPORTANT] diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index c3bbfff47791..4a402e761278 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -75,9 +75,31 @@ The Blazor project template: :::moniker range=">= aspnetcore-11.0" * Enables automatic Cross-Site Request Forgery (CSRF) protection middleware in apps built with `WebApplication.CreateBuilder`. The middleware inspects the `Sec-Fetch-Site` and `Origin` headers on unsafe HTTP methods and records a validation verdict on the request. Blazor server-side rendering (SSR) form posts enforce that verdict and return `400 Bad Request` for cross-origin form posts that aren't trusted. -* Implicitly adds token-based antiforgery services to the app when is called in the `Program` file. Antiforgery middleware isn't automatically included in the request processing pipeline without explicitly calling . +* Implicitly adds token-based antiforgery *services* to the app when is called in the `Program` file. -To explicitly add antiforgery middleware, call after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . +Antiforgery *middleware* isn't automatically included in the request processing pipeline without explicitly calling . + +To explicitly add Antiforgery Middleware, call after the call to . If there are calls to and , the call to must go between them. A call to must be placed after calls to and . + +Adding token-based antiforgery middleware doesn't replace the automatic header-based CSRF protection middleware. When an app calls , both defense mechanisms run for a form post: + +* The header-based CSRF protection middleware runs first and records its verdict. +* Antiforgery middleware performs token-based validation. + +The token-based result from antiforgery middleware is authoritative and overrides the earlier header-based CSRF middleware verdict. + +To disable the automatic header-based CSRF protection middleware, set the `DisableCsrfProtection` configuration key to true. For example, use the app settings file (`appsettings.json`) to disable the middleware: + +```json +{ + "DisableCsrfProtection": true +} +``` + +The the `DisableCsrfProtection` configuration setting can be supplied by any configuration source, including via an environment variable (`DisableCsrfProtection=true`). + +> [!WARNING] +> Disabling the automatic CSRF protection middleware removes the default header-based (`Sec-Fetch-Site`/`Origin`) protection for the entire app. Only disable it if you provide an alternative CSRF defense, such as explicitly adopting token-based antiforgery middleware by calling . For more information, see .