Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3385a02
[11.0 P6] Blazor Preview 6 coverage
guardrex Jul 8, 2026
ad5066e
Updates
guardrex Jul 8, 2026
06572b4
Updates
guardrex Jul 9, 2026
0709fa6
Apply suggestions from code review
guardrex Jul 9, 2026
3f0aa0d
Updates
guardrex Jul 9, 2026
9da4ca3
Updates
guardrex Jul 10, 2026
87a326a
Updates
guardrex Jul 10, 2026
5716816
Updates
guardrex Jul 10, 2026
f5aec5b
Apply suggestions from code review
guardrex Jul 10, 2026
6c107c8
EnvironmentBoundary renamed to EnvironmentView
guardrex Jul 13, 2026
559bda5
GetUriWithHash renamed to GetUriWithFragment
guardrex Jul 13, 2026
fc74348
Rename disableDomPreservation to preserveDom (with opposite meaning)
guardrex Jul 13, 2026
813cd55
Fixes to TempData and `[SupplyParameterFromSession]` persistence for …
guardrex Jul 13, 2026
8515716
Virtualization: Content Security Policy (CSP) compliance
guardrex Jul 13, 2026
ade5f55
Redundant `app.UseAntiforgery()` removed from Blazor Web templates
guardrex Jul 13, 2026
ff16f4f
Preloading and enhanced nav -AND- automatic CSRF middleware
guardrex Jul 13, 2026
8181f9a
Updates
guardrex Jul 13, 2026
e93e34e
Apply suggestions from code review
guardrex Jul 13, 2026
2871d6f
Updates
guardrex Jul 13, 2026
cd5340e
React to Virtualize CSP feedback
guardrex Jul 14, 2026
0b22693
Updates
guardrex Jul 14, 2026
e15f308
Updates
guardrex Jul 14, 2026
f8d9161
Updates
guardrex Jul 14, 2026
6056a9e
Reverting blazor/fundamentals/startup.md changes
guardrex Jul 14, 2026
71c2b83
Configure Blazor client behavior from the server
guardrex Jul 14, 2026
c6bd135
Move the Configure Blazor client behavior from the server section UP
guardrex Jul 14, 2026
2b72e4a
Add Blazor Virtualize can scroll to an item to What's New coverage
guardrex Jul 14, 2026
0cb5bb0
Updates to BWA template change for antiforgery
guardrex Jul 14, 2026
64b7044
Union coverage updates
guardrex Jul 14, 2026
a48d605
React to feedback
guardrex Jul 14, 2026
7c5615b
Add additional CSRF/antiforgery middleware coverage
guardrex Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 116 additions & 1 deletion aspnetcore/blazor/components/dynamiccomponent.md
Original file line number Diff line number Diff line change
@@ -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: 11/11/2025
ms.date: 07/10/2026
Comment thread
guardrex marked this conversation as resolved.
Resolved
uid: blazor/components/dynamiccomponent
---
# Dynamically-rendered ASP.NET Core Razor components
Expand Down Expand Up @@ -252,6 +253,120 @@ In the following example:

:::moniker-end

:::moniker range=">= aspnetcore-11.0"

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.

<!-- UPDATE 11.0 - Remove the NOTE at GA -->

> [!NOTE]
> During the preview of .NET 11, the following example requires the app's project file to specify the "`preview`" C# language version:
>
> ```xml
> <LangVersion>preview</LangVersion>
> ```

Consider the following C# union type, `SlotContent`, that accepts text, a <xref:Microsoft.AspNetCore.Components.MarkupString>, or a <xref:Microsoft.AspNetCore.Components.RenderFragment>:

```csharp
using Microsoft.AspNetCore.Components;

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 two implementations of the [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression), one through an assignment to a <xref:Microsoft.AspNetCore.Components.RenderFragment> (`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
<h2>@@ContentSwitch</h2>
Comment thread
guardrex marked this conversation as resolved.
Resolved

<div>
@ContentSwitch()
</div>

<h2>Inline <code>switch</code> expression</h2>

<div>
@switch (Content)
{
case string text:
@text
break;
case MarkupString html:
@html
break;
case RenderFragment fragment:
@fragment
break;
default:
<em>Empty slot!</em>
break;
}
</div>

@code {
[Parameter, EditorRequired]
public SlotContent Content { get; set; }

private RenderFragment ContentSwitch() => Content switch
{
string text => @<span>@text</span>,
MarkupString html => @<span>@html</span>,
RenderFragment fragment => fragment,
_ => @<em>Empty slot!</em>
};
}
```

<span aria-hidden="true">❌</span><span class="visually-hidden">Unsupported:</span> Using the preceding `Slot` component, the following component fails at run time with an <xref:System.InvalidCastException>:

```razor
@page "/slot-test-1"

<DynamicComponent Type="@TargetComponent" Parameters="@BadParameters" />

@code {
[Parameter]
public Type TargetComponent { get; set; } = typeof(Slot);

[Parameter]
public string RawTextPayload { get; set; } = "Hello from dynamic data!";

private Dictionary<string, object> BadParameters => new()
{
{ "Content", RawTextPayload }
};
}
```

> :::no-loc text="System.InvalidCastException: Unable to cast object of type 'System.String' to type 'SlotContent'.":::

<span aria-hidden="true">✔️</span><span class="visually-hidden">Supported:</span> To box the union, wrap the raw value explicitly into the union wrapper type (`new SlotContent(...)`), as the following example demonstrates.

`SlotTest2.razor`:

```razor
@page "/slot-test-2"

<DynamicComponent Type="@TargetComponent" Parameters="@ValidParameters" />

@code {
[Parameter]
public Type TargetComponent { get; set; } = typeof(Slot);

[Parameter]
public string RawTextPayload { get; set; } = "Hello from dynamic data!";

private Dictionary<string, object> ValidParameters => new()
{
{ "Content", new SlotContent(RawTextPayload) }
};
}
```

:::moniker-end

## Event callbacks (`EventCallback`)

Event callbacks (<xref:Microsoft.AspNetCore.Components.EventCallback>) can be passed to a <xref:Microsoft.AspNetCore.Components.DynamicComponent> in its parameter dictionary.
Expand Down
127 changes: 118 additions & 9 deletions aspnetcore/blazor/components/index.md
Original file line number Diff line number Diff line change
@@ -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/10/2026
Comment thread
guardrex marked this conversation as resolved.
Resolved
uid: blazor/components/index
---
# ASP.NET Core Razor components
Expand Down Expand Up @@ -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"

<!-- UPDATE 11.0 - Remove the following paragraph per resolution of the PU issue -->

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.
Expand Down Expand Up @@ -1171,6 +1164,122 @@ Quote &copy;2005 [Universal Pictures](https://www.uphe.com): [Serenity](https://

:::moniker-end

:::moniker range=">= aspnetcore-11.0"

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 <xref:Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddComponentParameter%2A?displayProperty=nameWithType>.

<!-- UPDATE 11.0 - Remove the NOTE at GA -->

> [!NOTE]
> During the preview of .NET 11, the following example requires the app's project file to specify the "`preview`" C# language version:
>
> ```xml
> <LangVersion>preview</LangVersion>
> ```

The following C# union type, `SlotContent`, accepts text, a <xref:Microsoft.AspNetCore.Components.MarkupString>, or a <xref:Microsoft.AspNetCore.Components.RenderFragment>:

```csharp
using Microsoft.AspNetCore.Components;

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 two implementations of the [C# `switch` expression](/dotnet/csharp/language-reference/operators/switch-expression), one through an assignment to a <xref:Microsoft.AspNetCore.Components.RenderFragment> (`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
<h2>@@ContentSwitch</h2>
Comment thread
guardrex marked this conversation as resolved.
Resolved

<div>
@ContentSwitch()
</div>

<h2>Inline <code>switch</code> expression</h2>

<div>
@switch (Content)
{
case string text:
@text
break;
case MarkupString html:
@html
break;
case RenderFragment fragment:
@fragment
break;
default:
<em>Empty slot!</em>
break;
}
</div>

@code {
[Parameter, EditorRequired]
public SlotContent Content { get; set; }

private RenderFragment ContentSwitch() => Content switch
{
string text => @<span>@text</span>,
MarkupString html => @<span>@html</span>,
RenderFragment fragment => fragment,
_ => @<em>Empty slot!</em>
};
}
```

The following `SlotExample` component uses the preceding `SlotContent` type and `Slot` component.

`SlotExample.razor`:

```razor
@page "/slot-example"

<h1>Plain text</h1>

<Slot Content="@("Simple plain text slot")" />

<h1><code>MarkupString</code></h1>

<Slot Content='@((MarkupString)"<strong>Bold HTML slot</strong>")' />

<h1><code>RenderFragment</code></h1>

<Slot Content="@content" />

@code {
private SlotContent content;

protected override void OnInitialized()
{
content = 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 Increment() => currentCount++;
}
```

<!-- UPDATE 11.0 - Remove the following paragraph per resolution of the PU issue -->

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.

<!-- UPDATE 13.0 - Remove the following cross-link when C# unions get some time on them (target removal for .NET 13) -->

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.
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ The correct culture-invariant formatting is applied for the given type (<xref:Sy
:::moniker range=">= aspnetcore-11.0"

> [!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

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/09/2026
Comment thread
guardrex marked this conversation as resolved.
Resolved
uid: blazor/js-interop/call-dotnet-from-javascript
---
# Call .NET methods from JavaScript functions in ASP.NET Core Blazor
Expand All @@ -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 <xref:blazor/js-interop/call-javascript-from-dotnet>.

:::moniker range=">= aspnetcore-11.0"

<!-- UPDATE 13.0 - Consider removing the following cross-link because the 'call JS from .NET' article
will maintain a versioned support remark beyond .NET 13. -->

> [!NOTE]
> [C# union types](/dotnet/csharp/language-reference/builtin-types/union) are supported.

:::moniker-end

## Invoke a static .NET method

To invoke a static .NET method from JavaScript (JS), use the JS functions:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/09/2026
Comment thread
guardrex marked this conversation as resolved.
Resolved
uid: blazor/js-interop/call-javascript-from-dotnet
---
# Call JavaScript functions from .NET methods in ASP.NET Core Blazor
Expand All @@ -26,12 +26,28 @@ For information on how to call .NET methods from JS, see <xref:blazor/js-interop

For the preceding .NET methods that invoke JS functions:

:::moniker range=">= aspnetcore-11.0"

* The function identifier (`String`) is relative to the global scope (`window`). To call `window.someScope.someFunction`, the identifier is `someScope.someFunction`. There's no need to register the function before it's called.
* Pass any number of JSON-serializable arguments in `Object[]` to a JS function.
* The cancellation token (`CancellationToken`) propagates a notification that operations should be canceled.
* `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/language-reference/builtin-types/union) are supported.

:::moniker-end

:::moniker range="< aspnetcore-11.0"

* The function identifier (`String`) is relative to the global scope (`window`). To call `window.someScope.someFunction`, the identifier is `someScope.someFunction`. There's no need to register the function before it's called.
* Pass any number of JSON-serializable arguments in `Object[]` to a JS function.
* The cancellation token (`CancellationToken`) propagates a notification that operations should be canceled.
* `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).

:::moniker-end
Comment thread
guardrex marked this conversation as resolved.
Resolved

For Blazor apps with prerendering enabled, which is the default for server-side apps, calling into JS isn't possible during prerendering. For more information, see the [Prerendering](#prerendering) section.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: 11/11/2025
ms.date: 07/09/2026
Comment thread
guardrex marked this conversation as resolved.
Resolved
uid: blazor/state-management/prerendered-state-persistence
---
# ASP.NET Core Blazor prerendered state persistence
Expand Down Expand Up @@ -143,6 +143,17 @@ In the following example that serializes state for multiple components of the sa
}
```

:::moniker-end

:::moniker range=">= aspnetcore-11.0"

> [!NOTE]
> A user-configured <xref:System.Text.Json.Serialization.JsonSerializerContext> 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.

Comment thread
guardrex marked this conversation as resolved.
Resolved
:::moniker-end

:::moniker range=">= aspnetcore-10.0"

## Serialize state for services

In the following example that serializes state for a dependency injection service:
Expand Down
Loading
Loading